Автор заметки, перевод которой мы сегодня публикуем, говорит, что с трудом удержался от того, чтобы не назвать её «Finally — возможность, которую все ждали», или как-то пошутить на эту тему. В итоге он решил обойтись без шуток и просто рассказать о по-настоящему важной и полезной возможности объектов Promise.


Если вы только начинаете осваивать JavaScript и не слишком хорошо знакомы с промисами (иногда их называют «обещаниями», «обещанными результатами», «Promise-объектами»), то, возможно, вам будут интересны наши предыдущие публикации на эту тему:

> Промисы в ES6: паттерны и анти-паттерны
> JavaScript: методы асинхронного программирования
> JavaScript ES8 и переход на async / await
> Async/await: 6 причин забыть о промисах
> Побег из ада async/await
> JavaScript ES6: пишем меньше — делаем больше
> Руководство по промисам для тех, кто хочет в них разобраться
> Конструкция async/await в JavaScript: сильные стороны, подводные камни и особенности использования
> Использование промисов в JavaScript

Метод Promise.prototype.finally


Методу Promise.prototype.finally посвящён пункт 25.6.5.3 стандарта ECMAScript 2018. По информации ресурса caniuse.com, уровень поддержки этого метода составляет примерно 81%. Этим методом можно пользоваться и в среде Node.js.

Метод промисов finally — это одно из важнейших новшеств стандарта, которое позволяет задавать функцию, которая будет выполнена независимо от результата промиса. Такая функция выполнится и при успешном разрешении промиса и при его отклонении.
Рассмотрим пример:

const myPromise = new Promise((resolve, reject) => {

 setTimeout(() => { resolve('success!!!'); }, 2000);

});

Это — совершенно обычный промис, который разрешается через 2000 миллисекунд. Если после этого нужно выполнить какое-то действие — нам понадобится блок then:

myPromise.then(

 result => { console.log(result) },

 failMessage => { console.log(failMessage) }

);

Методу then переданы две анонимных функции. Первая будет выполнена в том случае, если промис будет успешно разрешён. Вторая — при его отклонении. Наш промис всегда завершает работу успешно, в консоль всегда будет выводиться сообщение success!!!. Всё это очень хорошо, но как быть, если надо, чтобы некие действия выполнялись бы и после отклонения промиса, и после успешного завершения его работы? Тут нам и поможет метод finally:

const myPromise = new Promise((resolve, reject) => {

 setTimeout(() => { resolve('success!!!'); }, 2000);

});


myPromise.then(

 result => { console.log(result) },

 failMessage => { console.log(failMessage) }

).finally(finallyMessage => { console.log('FINALLY!!')});

Независимо от того, как промис завершит работу, в консоль, помимо соответствующего сообщения, будет выведен текст FINALLY!!, что говорит нам о том, что функция обратного вызова, переданная методу finally, срабатывает в любом случае. Для того чтобы в этом убедиться — можете поэкспериментировать.

Итоги


То, что в ES2018 появился метод Promise.prototype.finally, говорит о том, что в обозримом будущем можно ожидать очень высокого уровня его поддержки браузерами. Это значит, что то, для чего раньше приходилось использовать вспомогательные инструменты, созданные сторонними разработчиками, теперь может быть реализовано стандартными средствами.

В каких ситуациях может пригодиться метод Promise.prototype.finally? Например — если при запуске промиса, используемого для загрузки чего-либо, начинает воспроизводиться некая анимация, в finally можно завершить эту анимацию. В блоке finally можно, например, закрыть некое модальное окно. На самом деле, существует множество ситуаций, в которых метод finally может оказаться полезным.

Уважаемые читатели! Пользовались ли вы заменителями метода промисов finally до появления его стандартных реализаций?

Комментарии (12)


  1. ZLBRSTN
    24.10.2018 12:15
    +1

    Promise.then(...).catch(...).then(/* Не то же самое? */)


    1. mayorovp
      24.10.2018 12:27
      +3

      Конкретно в приведенном случае- да, то же самое. Потому что значение промиса нигде не используется. А вот если оно используется, то так красиво сделать не получится. Попробуйте заменить вот это:


      fetch("https://jsonplaceholder.typicode.com/todos/1")
          .then(x => x.json())
          .finally(() => console.log("request complete"))
          .then(x => console.log("title: " + x.title))
          .catch(x => console.error(x));


    1. rzcoder
      26.10.2018 00:33

      Не то же самое. catch тоже может кинуть экцепшн и тогда then не сработает.


  1. agentx001
    24.10.2018 17:09

    Промисы прекрасны. Но вот раздражает, что нельзя завершить работу где-то в середине цепочки как-то кроме выбрасывания исключения.


    1. yar3333
      24.10.2018 18:09

      Так можно же просто вернуть промис, который никогда не разрешится. Или я чего-то не понимаю?


      1. Taraflex
        24.10.2018 19:02
        +1

        Разве так память не потечет?


        1. mayorovp
          24.10.2018 19:10
          +1

          Если эту цепочку никуда не сохранять — то нет, не потечет.


    1. mayorovp
      24.10.2018 19:11

      А откуда такое требование — "как-то кроме выбрасывания исключения"? Чем исключение не устроило?


      Или смущает не исключение, а сама операция throw? В таком случае return Promise.reject(...) в помощь.


  1. Staltec
    24.10.2018 20:31
    +2

    Лучше бы реализовали Promise.prototype.cancel() который реализован в том же Bluebird. Очень полезен в случаях когда установка стейта react-компонента уже неактуальна потому как компонент успел размонтироваться пока промис исполнялся.


    1. dfuse
      24.10.2018 20:45

      Это еще надо как то увязывать с сигналами отменяемых fetch.


    1. mayorovp
      26.10.2018 08:39

      Вот только в том же Bluebird один раз уже пришлось менять семантику отмены. И все равно остались странности, из-за которых отмена обещаний и выключена по умолчанию.


  1. Odrin
    25.10.2018 17:04
    +1

    одно из важнейших новшеств стандарта
    Которое было еще во времена jQuery Deferred и Q ($q из первого angular). Для меня до сих пор остается загадкой, почему finally не вошел в стандарт с самого начала, ведь это удобная и сама-собой напрашивающаяся функция.