Меню

Promise как получить promise result

Promise

Материал на этой странице устарел, поэтому скрыт из оглавления сайта.

Более новая информация по этой теме находится на странице https://learn.javascript.ru/promise-basics.

Promise (обычно их так и называют «промисы») – предоставляют удобный способ организации асинхронного кода.

В современном JavaScript промисы часто используются в том числе и неявно, при помощи генераторов, но об этом чуть позже.

Что такое Promise?

Promise – это специальный объект, который содержит своё состояние. Вначале pending («ожидание»), затем – одно из: fulfilled («выполнено успешно») или rejected («выполнено с ошибкой»).

На promise можно навешивать колбэки двух типов:

  • onFulfilled – срабатывают, когда promise в состоянии «выполнен успешно».
  • onRejected – срабатывают, когда promise в состоянии «выполнен с ошибкой».

Способ использования, в общих чертах, такой:

  1. Код, которому надо сделать что-то асинхронно, создаёт объект promise и возвращает его.
  2. Внешний код, получив promise , навешивает на него обработчики.
  3. По завершении процесса асинхронный код переводит promise в состояние fulfilled (с результатом) или rejected (с ошибкой). При этом автоматически вызываются соответствующие обработчики во внешнем коде.

Синтаксис создания Promise :

Универсальный метод для навешивания обработчиков:

  • onFulfilled – функция, которая будет вызвана с результатом при resolve .
  • onRejected – функция, которая будет вызвана с ошибкой при reject .

С его помощью можно назначить как оба обработчика сразу, так и только один:

Для того, чтобы поставить обработчик только на ошибку, вместо .then(null, onRejected) можно написать .catch(onRejected) – это то же самое.

Если в функции промиса происходит синхронный throw (или иная ошибка), то вызывается reject :

Посмотрим, как это выглядит вместе, на простом примере.

Пример с setTimeout

Возьмём setTimeout в качестве асинхронной операции, которая должна через некоторое время успешно завершиться с результатом «result»:

В результате запуска кода выше – через 1 секунду выведется «Fulfilled: result».

А если бы вместо resolve(«result») был вызов reject(«error») , то вывелось бы «Rejected: error». Впрочем, как правило, если при выполнении возникла проблема, то reject вызывают не со строкой, а с объектом ошибки типа new Error :

Конечно, вместо setTimeout внутри функции промиса может быть и запрос к серверу и ожидание ввода пользователя, или другой асинхронный процесс. Главное, чтобы по своему завершению он вызвал resolve или reject , которые передадут результат обработчикам.

Функции resolve/reject принимают ровно один аргумент – результат/ошибку.

Именно он передаётся обработчикам в .then , как можно видеть в примерах выше.

Promise после reject/resolve – неизменны

Заметим, что после вызова resolve/reject промис уже не может «передумать».

Когда промис переходит в состояние «выполнен» – с результатом (resolve) или ошибкой (reject) – это навсегда.

В результате вызова этого кода сработает только первый обработчик then , так как после вызова resolve промис уже получил состояние (с результатом), и в дальнейшем его уже ничто не изменит.

Последующие вызовы resolve/reject будут просто проигнорированы.

А так – наоборот, ошибка будет раньше:

Промисификация

Промисификация – это когда берут асинхронную функциональность и делают для неё обёртку, возвращающую промис.

После промисификации использование функциональности зачастую становится гораздо удобнее.

В качестве примера сделаем такую обёртку для запросов при помощи XMLHttpRequest.

Функция httpGet(url) будет возвращать промис, который при успешной загрузке данных с url будет переходить в fulfilled с этими данными, а при ошибке – в rejected с информацией об ошибке:

Как видно, внутри функции объект XMLHttpRequest создаётся и отсылается как обычно, при onload/onerror вызываются, соответственно, resolve (при статусе 200) или reject .

Заметим, что ряд современных браузеров уже поддерживает fetch – новый встроенный метод для AJAX-запросов, призванный заменить XMLHttpRequest. Он гораздо мощнее, чем httpGet . И – да, этот метод использует промисы. Полифил для него доступен на https://github.com/github/fetch.

Цепочки промисов

«Чейнинг» (chaining), то есть возможность строить асинхронные цепочки из промисов – пожалуй, основная причина, из-за которой существуют и активно используются промисы.

Например, мы хотим по очереди:

  1. Загрузить данные посетителя с сервера (асинхронно).
  2. Затем отправить запрос о нём на github (асинхронно).
  3. Когда это будет готово, вывести его github-аватар на экран (асинхронно).
  4. …И сделать код расширяемым, чтобы цепочку можно было легко продолжить.

Вот код для этого, использующий функцию httpGet , описанную выше:

Самое главное в этом коде – последовательность вызовов:

При чейнинге, то есть последовательных вызовах .then…then…then , в каждый следующий then переходит результат от предыдущего. Вызовы console.log оставлены, чтобы при запуске можно было посмотреть конкретные значения, хотя они здесь и не очень важны.

Если очередной then вернул промис, то далее по цепочке будет передан не сам этот промис, а его результат.

  1. Функция в первом then возвращает «обычное» значение user . Это значит, что then возвратит промис в состоянии «выполнен» с user в качестве результата. Он станет аргументом в следующем then .
  2. Функция во втором then возвращает промис (результат нового вызова httpGet ). Когда он будет завершён (может пройти какое-то время), то будет вызван следующий then с его результатом.
  3. Третий then ничего не возвращает.

Схематично его работу можно изобразить так:

Значком «песочные часы» помечены периоды ожидания, которых всего два: в исходном httpGet и в подвызове далее по цепочке.

Если then возвращает промис, то до его выполнения может пройти некоторое время, оставшаяся часть цепочки будет ждать.

То есть, логика довольно проста:

  • В каждом then мы получаем текущий результат работы.
  • Можно его обработать синхронно и вернуть результат (например, применить JSON.parse ). Или же, если нужна асинхронная обработка – инициировать её и вернуть промис.

Обратим внимание, что последний then в нашем примере ничего не возвращает. Если мы хотим, чтобы после setTimeout (*) асинхронная цепочка могла быть продолжена, то последний then тоже должен вернуть промис. Это общее правило: если внутри then стартует новый асинхронный процесс, то для того, чтобы оставшаяся часть цепочки выполнилась после его окончания, мы должны вернуть промис.

В данном случае промис должен перейти в состояние «выполнен» после срабатывания setTimeout .

Строку (*) для этого нужно переписать так:

Теперь, если к цепочке добавить ещё then , то он будет вызван после окончания setTimeout .

Перехват ошибок

Выше мы рассмотрели «идеальный случай» выполнения, когда ошибок нет.

А что, если github не отвечает? Или JSON.parse бросил синтаксическую ошибку при обработке данных?

Правило здесь очень простое.

При возникновении ошибки – она отправляется в ближайший обработчик onRejected .

Такой обработчик нужно поставить через второй аргумент .then(. onRejected) или, что то же самое, через .catch(onRejected) .

Чтобы поймать всевозможные ошибки, которые возникнут при загрузке и обработке данных, добавим catch в конец нашей цепочки:

В примере выше ошибка возникает в первом же httpGet , но catch с тем же успехом поймал бы ошибку во втором httpGet или в JSON.parse .

Принцип очень похож на обычный try..catch : мы делаем асинхронную цепочку из .then , а затем, в том месте кода, где нужно перехватить ошибки, вызываем .catch(onRejected) .

Обработчик .catch(onRejected) получает ошибку и должен обработать её.

Есть два варианта развития событий:

  1. Если ошибка не критичная, то onRejected возвращает значение через return , и управление переходит в ближайший .then(onFulfilled) .
  2. Если продолжить выполнение с такой ошибкой нельзя, то он делает throw , и тогда ошибка переходит в следующий ближайший .catch(onRejected) .

Это также похоже на обычный try..catch – в блоке catch ошибка либо обрабатывается, и тогда выполнение кода продолжается как обычно, либо он делает throw . Существенное отличие – в том, что промисы асинхронные, поэтому при отсутствии внешнего .catch ошибка не «вываливается» в консоль и не «убивает» скрипт.

Ведь возможно, что новый обработчик .catch будет добавлен в цепочку позже.

Промисы в деталях

Самым основным источником информации по промисам является, разумеется, стандарт.

Чтобы наше понимание промисов было полным, и мы могли с лёгкостью разрешать сложные ситуации, посмотрим внимательнее, что такое промис и как он работает, но уже не в общих словах, а детально, в соответствии со стандартом ECMAScript.

Согласно стандарту, у объекта new Promise(executor) при создании есть четыре внутренних свойства:

  • PromiseState – состояние, вначале «pending».
  • PromiseResult – результат, при создании значения нет.
  • PromiseFulfillReactions – список функций-обработчиков успешного выполнения.
  • PromiseRejectReactions – список функций-обработчиков ошибки.

Когда функция-executor вызывает reject или resolve , то PromiseState становится «resolved» или «rejected» , а все функции-обработчики из соответствующего списка перемещаются в специальную системную очередь «PromiseJobs» .

Эта очередь автоматически выполняется, когда интерпретатору «нечего делать». Иначе говоря, все функции-обработчики выполнятся асинхронно, одна за другой, по завершении текущего кода, примерно как setTimeout(. 0) .

Исключение из этого правила – если resolve возвращает другой Promise . Тогда дальнейшее выполнение ожидает его результата (в очередь помещается специальная задача), и функции-обработчики выполняются уже с ним.

Добавляет обработчики в списки один метод: .then(onResolved, onRejected) . Метод .catch(onRejected) – всего лишь сокращённая запись .then(null, onRejected) .

  • Если PromiseState == «pending» , то есть промис ещё не выполнен, то обработчики добавляются в соответствующие списки.
  • Иначе обработчики сразу помещаются в очередь на выполнение.

Здесь важно, что обработчики можно добавлять в любой момент. Можно до выполнения промиса (они подождут), а можно – после (выполнятся в ближайшее время, через асинхронную очередь).

Источник статьи: http://learn.javascript.ru/promise

How to extract data out of a Promise

I have a promise that returns data and I want to save that in variables. Is this impossible in JavaScript because of the async nature and do I need to use onResolve as a callback?

Can I somehow use this (e.g. wrap it with async/await):

instead of this?

Note: Bluebird library is used instead of native implementation, and I can’t change from Promise to asnyc/await or Generators.

5 Answers 5

NO you can’t get the data synchronously out of a promise like you suggest in your example. The data must be used within a callback function. Alternatively in functional programming style the promise data could be map()ed over.

If your are OK using async/await (you should it’s awesome) then you can write code that looks synchronous yet retain the asynchronicity of a promise (see @loganfsmyth comments).

Overall since you are already using ES6 I assume you are also using a transpiler. In which case you should definitely give async/await a try. Just be sure to weight in the decision that as today they are not yet a ratified specification.

While you can get a value from an awaited Promise inside an async function (simply because it pauses the function to await a result), you can’t ever get a value directly «out» of a Promise and back into the same scope as the code that created the Promise itself.

That’s because «out of» would mean trying to take something that exists in the future (the eventually resolved value) and putting it into a context (synchronous variable assignment) that already happened in the past.

That is, time-travel. And even if time-travel were possible, it probably wouldn’t be a good coding practice because time travel can be very confusing.:)

In general, if you find yourself feeling like you need to do this, it’s good sign that you need to refactor something. Note that what you’re doing with «result => result.data» here:

..is already a case of you working with (literally, mapping over) the value by passing it to a function. But, assuming that «// rest of script» does something important related to this value, you probably want to continue mapping over the now updated value with yet another function that then does something side-effect-y with the value (like display the data on the screen).

«doSomethingWithData» will be called (if it’s ever called) at some unknown point in the future. Which is why it’s a good practice to clearly encapsulate all that behavior into a specific function and then hook that function up to the Promise chain.

It’s honestly better this way, because it requires you to clearly declare a particular sequence of events that will happen, explicitly separated out from the first run through all of your application code’s execution.

To put it another way, imagine this scenario, hypothetically executed in the global, top-level scope:

What would you expect to happen there? There are two possibilities, and both of them are bad.

  1. Your entire program would have to halt and wait for the Promise to execute before it could know what «foo» & «bar» would. nay, might be. (this is what «await,» inside an async function, does in fact do: it pauses the entire function execution until the value is available or an the error is thrown)
  2. foo and bar would just be undefined (this is what actually happens), since, as executed synchronously, they’d just be non-existent properties of the top-level Promise object (which is not itself a «value,» but rather a quasi-Monadic wrapper around getting an eventual value OR an error) which most likely doesn’t even contain a value yet.

Источник статьи: http://stackoverflow.com/questions/36911241/how-to-extract-data-out-of-a-promise

How can I access the value of a promise?

I’m looking at this example from Angular’s documentation for $q , but I think this probably applies to promises in general. The example below is copied verbatim from their documentation with their comment included:

I’m not clear how this works. If I can call .then() on the result of the first .then() , chaining them, which I know I can, then promiseB is a promise object, of type Object . It is not a Number . So what do they mean by «its value will be the result of promiseA incremented by 1»?

Am I supposed to access that as promiseB.value or something like that? How can the success callback return a promise AND return «result + 1»? I’m missing something.

14 Answers 14

promiseA ‘s then function returns a new promise ( promiseB ) that is immediately resolved after promiseA is resolved, its value is the value of the what is returned from the success function within promiseA .

In this case promiseA is resolved with a value — result and then immediately resolves promiseB with the value of result + 1 .

Accessing the value of promiseB is done in the same way we accessed the result of promiseA .

As of ECMAScript 2016 (ES7, 2016), async / await is standard in JavaScript, which allows an alternative syntax to the approach described above. You can now write:

Now there is no promiseB, because we’ve unwrapped the result from promiseA using await , and you can work with it directly.

However, await can only be used inside an async function. So to zoom out slightly, the above would have to be contained like so:

And, for clarity, the return value of the function doSomething in this example is still a promise — because async functions return promises. So if you wanted to access that return value, you would have to do result = await doSomething() , which you can only do inside another async function. Basically, only in a parent async context can you directly access the value produced from a child async context.

When a promise is resolved/rejected, it will call its success/error handler:

The then method also returns a promise: promiseB, which will be resolved/rejected depending on the return value from the success/error handler from promiseA.

There are three possible values that promiseA’s success/error handlers can return that will affect promiseB’s outcome:

  1. Return nothing → PromiseB is resolved immediately, and undefined is passed to the success handler of promiseB
  2. Return a value → PromiseB is resolved immediately, and the value is passed to the success handler of promiseB
  3. Return a promise → When resolved, promiseB will be resolved. When rejected, promiseB will be rejected. The value passed to the promiseB’s then handler will be the result of the promise

Armed with this understanding, you can make sense of the following:

The then call returns promiseB immediately.

When promiseA is resolved, it will pass the result to promiseA’s success handler.

Since the return value is promiseA’s result + 1, the success handler is returning a value (option 2 above), so promiseB will resolve immediately, and promiseB’s success handler will be passed promiseA’s result + 1.

Источник статьи: http://stackoverflow.com/questions/29516390/how-can-i-access-the-value-of-a-promise

Promise как получить promise result

Ранее мы рассмотрели, как из функции промиса мы можем передать во вне результат асинхронной операции:

Теперь получим это значение. Для получения результата операции промиса применяется функция then() объекта Promise :

Первый параметр функции — onFulfilled представляет функцию, которая выполняется при успешном завершении промиса и в качестве параметра получает переданные в resolve() данные.

Второй параметр функции — onRejected представляет функцию, которая выполняется при возникновении ошибки и в качестве параметра получает переданные в reject() данные.

Функция then() возвращает также объект Promise .

Так, получим переданные данные:

То есть параметр value здесь будет представлять строку «Привет мир!» , которая передается в resolve(«Привет мир!») . В итоге консольный вывод будет выглядеть следующим образом:

При этом нам необязательно вообще передавать в resolve() какое-либо значение. Возможно, асинхронная операция просто выполняется и передает во вне никакого результата.

В данном случае функция в промисе вычисляет сумму чисел x и y и выводит результат на консоль.

Метод Promise.resolve

Иногда требуется просто вернуть из промиса некоторое значение. Для этого можно использовать метод Promise.resolve() . В этот метод передается возвращаемое из промиса значение. Метод Promise.resolve() возвращает объект Promise:

Определение промиса через функцию

Нередко промис определяется через функцию, которая возвращет объект Promise. Например:

Здесь функция sum() принимает два числа и возвращает промис, который инкапсулирует операцию сумму этих чисел. После вычисления сумма чисел передается в resolve() , соответственно мы ее затем можем получить через метод then() . Определение промиса через функцию позволяет нам, с одной стороны, при вызове функции передавать разные значения. А с другой стороны, работать с результатом этой функции как с промисом и настроить при каждом конкретном вызове обработку полученного значения.

Результат работы программы:

Однако, что если у нас совпадает принцип обработки полученного из асинхронной функции значения?

В этом случае логика обработки будет повторяться. Но поскольку метод then() также возвращает объект Promise, то мы можем сделать следующим образом:

Гибкая настройка функции

А что, если мы хотим, чтобы у программиста был выбор: если он хочет, то может определить свой обработчик, а если нет, то применяется некоторый обработчик по умолчанию. В этом случае мы можем определить функцию обработчика в качестве параметра функции, а если он не передан, то устанавливать обработчик по умолчанию:

Источник статьи: http://metanit.com/web/javascript/17.2.php

Promise

Сводка

Объект Promise используется для отложенных и асинхронных вычислений.

Интерактивный пример

Синтаксис

Параметры

Объект функции с двумя аргументами resolve и reject . Функция executor получает оба аргумента и выполняется сразу, ещё до того как конструктор вернёт созданный объект. Первый аргумент ( resolve ) вызывает успешное исполнение промиса, второй ( reject ) отклоняет его. Обычно функция executor описывает выполнение какой-то асинхронной работы, по завершении которой необходимо вызвать функцию resolve или reject . Обратите внимание, что возвращаемое значение функции executor игнорируется.

Описание

Интерфейс Promise (промис) представляет собой обёртку для значения, неизвестного на момент создания промиса. Он позволяет обрабатывать результаты асинхронных операций так, как если бы они были синхронными: вместо конечного результата асинхронного метода возвращается своего рода обещание (дословный перевод слова «промис») получить результат в некоторый момент в будущем.

Promise может находиться в трёх состояниях:

  • ожидание (pending): начальное состояние, не исполнен и не отклонён.
  • исполнено (fulfilled): операция завершена успешно.
  • отклонено (rejected): операция завершена с ошибкой.

При создании промис находится в ожидании (pending), а затем может стать исполненным (fulfilled), вернув полученный результат (значение), или отклонённым (rejected), вернув причину отказа. В любом из этих случаев вызывается обработчик, прикреплённый к промису методом then . (Если в момент назначения обработчика промис уже исполнен или отклонён, обработчик всё равно будет вызван, т.е. асинхронное исполнение промиса и назначение обработчика не будет происходить в «состоянии гонки», как, например, в случае с событиями в DOM.)

Так как методы Promise.prototype.then() и Promise.prototype.catch() сами возвращают промис, их можно вызывать цепочкой, создавая соединения.

Примечание: говорят, что промис находится в состоянии завершён (settled) когда он или исполнен или отклонён, т.е. в любом состоянии, кроме ожидания (это лишь форма речи, не являющаяся настоящим состоянием промиса). Также можно встретить термин исполнен (resolved) — это значит что промис завершён или «заблокирован» в ожидании завершения другого промиса. В статье состояния и fates приводится более подробное описание терминологии.

Свойства

Значение свойства всегда равно 1 (количество аргументов конструктора).

Представляет прототип для конструктора Promise .

Методы

Ожидает исполнения всех промисов или отклонения любого из них.

Возвращает промис, который исполнится после исполнения всех промисов в iterable . В случае, если любой из промисов будет отклонён, Promise.all будет также отклонён.

Ожидает завершения всех полученных промисов (как исполнения так и отклонения).

Возвращает промис, который исполняется когда все полученные промисы завершены (исполнены или отклонены), содержащий массив результатов исполнения полученных промисов.

Ожидает исполнения или отклонения любого из полученных промисов.

Возвращает промис, который будет исполнен или отклонён с результатом исполнения первого исполненного или отклонённого промиса из iterable .

Возвращает промис, отклонённый из-за reason .

Возвращает промис, исполненный с результатом value .

Создание промиса

Объект Promise создаётся при помощи ключевого слова new и своего конструктора. Конструктор Promise принимает в качестве аргумента функцию, называемую «исполнитель» (executor function). Эта функция должна принимать две функции-колбэка в качестве параметров. Первый из них ( resolve ) вызывается, когда асинхронная операция завершилась успешно и вернула результат своего исполнения в виде значения. Второй колбэк ( reject ) вызывается, когда операция не удалась, и возвращает значение, указывающее на причину неудачи, чаще всего объект ошибки.

Чтобы снабдить функцию функциональностью промисов, нужно просто вернуть в ней объект Promise :

Источник статьи: http://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Promise

Промисы

Представьте, что вы известный певец, которого фанаты постоянно донимают расспросами о предстоящем сингле.

Чтобы получить передышку, вы обещаете разослать им сингл, когда он будет выпущен. Вы даёте фанатам список, в который они могут записаться. Они могут оставить там свой e-mail, чтобы получить песню, как только она выйдет. И даже больше: если что-то пойдёт не так, например, в студии будет пожар и песню выпустить не выйдет, они также получат уведомление об этом.

Все счастливы! Вы счастливы, потому что вас больше не донимают фанаты, а фанаты могут больше не беспокоиться, что пропустят новый сингл.

Это аналогия из реальной жизни для ситуаций, с которыми мы часто сталкиваемся в программировании:

  1. Есть «создающий» код, который делает что-то, что занимает время. Например, загружает данные по сети. В нашей аналогии это – «певец».
  2. Есть «потребляющий» код, который хочет получить результат «создающего» кода, когда он будет готов. Он может быть необходим более чем одной функции. Это – «фанаты».
  3. Promise (по англ. promise , будем называть такой объект «промис») – это специальный объект в JavaScript, который связывает «создающий» и «потребляющий» коды вместе. В терминах нашей аналогии – это «список для подписки». «Создающий» код может выполняться сколько потребуется, чтобы получить результат, а промис делает результат доступным для кода, который подписан на него, когда результат готов.

Аналогия не совсем точна, потому что объект Promise в JavaScript гораздо сложнее простого списка подписок: он обладает дополнительными возможностями и ограничениями. Но для начала и такая аналогия хороша.

Синтаксис создания Promise :

Функция, переданная в конструкцию new Promise , называется исполнитель (executor). Когда Promise создаётся, она запускается автоматически. Она должна содержать «создающий» код, который когда-нибудь создаст результат. В терминах нашей аналогии: исполнитель – это «певец».

Её аргументы resolve и reject – это колбэки, которые предоставляет сам JavaScript. Наш код – только внутри исполнителя.

Когда он получает результат, сейчас или позже – не важно, он должен вызвать один из этих колбэков:

  • resolve(value) — если работа завершилась успешно, с результатом value .
  • reject(error) — если произошла ошибка, error – объект ошибки.

Итак, исполнитель запускается автоматически, он должен выполнить работу, а затем вызвать resolve или reject .

У объекта promise , возвращаемого конструктором new Promise , есть внутренние свойства:

  • state («состояние») — вначале «pending» («ожидание»), потом меняется на «fulfilled» («выполнено успешно») при вызове resolve или на «rejected» («выполнено с ошибкой») при вызове reject .
  • result («результат») — вначале undefined , далее изменяется на value при вызове resolve(value) или на error при вызове reject(error) .

Так что исполнитель по итогу переводит promise в одно из двух состояний:

Позже мы рассмотрим, как «фанаты» узнают об этих изменениях.

Ниже пример конструктора Promise и простого исполнителя с кодом, дающим результат с задержкой (через setTimeout ):

Мы можем наблюдать две вещи, запустив код выше:

  1. Функция-исполнитель запускается сразу же при вызове new Promise .
  2. Исполнитель получает два аргумента: resolve и reject — это функции, встроенные в JavaScript, поэтому нам не нужно их писать. Нам нужно лишь позаботиться, чтобы исполнитель вызвал одну из них по готовности.

Спустя одну секунду «обработки» исполнитель вызовет resolve(«done») , чтобы передать результат:

Это был пример успешно выполненной задачи, в результате мы получили «успешно выполненный» промис.

А теперь пример, в котором исполнитель сообщит, что задача выполнена с ошибкой:

Подведём промежуточные итоги: исполнитель выполняет задачу (что-то, что обычно требует времени), затем вызывает resolve или reject , чтобы изменить состояние соответствующего Promise .

Промис – и успешный, и отклонённый будем называть «завершённым», в отличие от изначального промиса «в ожидании».

Исполнитель должен вызвать что-то одно: resolve или reject . Состояние промиса может быть изменено только один раз.

Все последующие вызовы resolve и reject будут проигнорированы:

Идея в том, что задача, выполняемая исполнителем, может иметь только один итог: результат или ошибку.

Также заметим, что функция resolve / reject ожидает только один аргумент (или ни одного). Все дополнительные аргументы будут проигнорированы.

В случае, если что-то пошло не так, мы должны вызвать reject . Это можно сделать с аргументом любого типа (как и resolve ), но рекомендуется использовать объект Error (или унаследованный от него). Почему так? Скоро нам станет понятно.

Обычно исполнитель делает что-то асинхронное и после этого вызывает resolve / reject , то есть через какое-то время. Но это не обязательно, resolve или reject могут быть вызваны сразу:

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

Такая ситуация нормальна. Мы сразу получим успешно завершённый Promise .

Свойства state и result – это внутренние свойства объекта Promise и мы не имеем к ним прямого доступа. Для обработки результата следует использовать методы .then / .catch / .finally , про них речь пойдёт дальше.

Потребители: then, catch

Объект Promise служит связующим звеном между исполнителем («создающим» кодом или «певцом») и функциями-потребителями («фанатами»), которые получат либо результат, либо ошибку. Функции-потребители могут быть зарегистрированы (подписаны) с помощью методов .then и .catch .

Наиболее важный и фундаментальный метод – .then .

Первый аргумент метода .then – функция, которая выполняется, когда промис переходит в состояние «выполнен успешно», и получает результат.

Второй аргумент .then – функция, которая выполняется, когда промис переходит в состояние «выполнен с ошибкой», и получает ошибку.

Например, вот реакция на успешно выполненный промис:

Источник статьи: http://learn.javascript.ru/promise-basics

Promise

Как уйти за значением выражения и вернуться, когда оно будет доступно.

Эта документация связана с понятием асинхронности в JavaScript. Зачем нужен асинхронный код и как он работает описано в обзорной статье «Асинхронность в JS».

Кратко

Промис (Promise) — специальный объект JavaScript, который используется для написания и обработки асинхронного кода.

Асинхронные функции возвращают объект Promise в качестве значения. Внутри промиса работает асинхронная операция, которая управляет его состоянием.

Промис может находиться в одном из трёх состояний:

  • pending — стартовое состояние, операция стартовала;
  • fulfilled — получен результат;
  • rejected — ошибка.

Поменять состояние можно только один раз: перейти из pending либо в fulfilled , либо в rejected :

У промиса есть методы then ( ) и catch ( ) , которые позволяют выполнять код при изменении его состояния.

Как пишется

Промис создаётся с помощью конструктора.

В конструктор передаётся функция-исполнитель асинхронной операции (англ. executor). Она вызывается сразу после создания промиса. Задача этой функции — выполнить асинхронную операцию и перевести состояние промиса в fulfilled (успех) или rejected (ошибка).

Изменить состояние промиса можно, вызвав колбэки, переданные аргументами в функцию:

  • первый параметр (в примере кода назван resolve ) — колбэк для перевода промиса в состояние fulfilled , при его вызове аргументом передаётся результат операции;
  • второй параметр (в примере кода назван reject ) — колбэк для перевода промиса в состояние rejected , при его вызове аргументом передаётся информация об ошибке.

Как понять

Промис решает задачу выполнения кода, который зависит от результата асинхронной операции.

Промис устроен таким образом, что рычаги управления его состоянием остаются у асинхронной функции. После создания, промис находится в состоянии ожидания pending . Когда асинхронная операция завершается, функция переводит промис в состояние успеха fulfilled или ошибки rejected .

С помощью методов then ( ) , catch ( ) и finally ( ) мы можем реагировать на изменение состояния промиса и выполнять код.

Пример ниже показывает состояние промиса, который создаётся при нажатии на кнопку купить. Промис случайным образом завершается успехом или ошибкой:

Методы

В работе мы чаще используем промисы, чем создаём. Использовать промис — значит выполнять код при изменении состояния промиса.

Существует три метода, которые позволяют реагировать на изменение промиса:

Подробнее о работе then ( ) читайте в статье Promise. Метод then ( ) .

Метод then ( ) используют, чтобы выполнить код после успешного выполнения асинхронной операции.

Например, мы запросили у сервера список фильмов и хотим отобразить их на экране, когда сервер получит результат. В этом случае:

  • асинхронная операция — запрос данных у сервера;
  • код, который мы хотим выполнить после её завершения, — отрисовка списка.

Метод then ( ) принимает в качестве аргумента функцию-колбэк, которая выполняется сразу после того, как промис поменял состояние на fulfilled . Параметр колбэка содержит результат выполнения операции:

В коде выше, асинхронная функция fetch ( ) возвращает промис, к которому применяется метод then . При его выполнении в переменной movies будет ответ сервера.

Подробнее о работе catch ( ) читайте в статье Promise. Метод catch ( ) .

Метод catch ( ) используют, чтобы выполнить код в случае ошибки при выполнении асинхронной операции.

Например, мы запросили у сервера список фильмов и хотим показать экран обрыва соединения, если произошла ошибка. В этом случае:

  • асинхронная операция — запрос данных у сервера;
  • код, который мы хотим выполнить при ошибке — экран обрыва соединения.

Метод catch ( ) принимает в качестве аргумента функцию-колбэк, которая выполняется сразу после того, как промис поменял состояние на rejected . Параметр колбэка содержит экземпляр ошибки:

В коде выше, асинхронная функция fetch ( ) возвращает промис, к которому применяется метод catch ( ) . При его выполнении в переменной error будет экземпляр ошибки.

Подробнее о работе finally ( ) читайте в статье Promise. Метод finally ( ) .

Метод finally ( ) используют, чтобы выполнить код при завершении асинхронной операции. Он будет выполнен вне зависимости от того, была ли операция успешной или завершилась ошибкой.

Самый частый сценарий использования finally ( ) — работа с индикаторами загрузки. Перед началом асинхронной операции разработчик включает индикатор загрузки. Индикатор нужно убрать вне зависимости от того, как завершилась операция. Если этого не сделать, то пользователь не сможет взаимодействовать с интерфейсом.

Метод finally ( ) принимает в качестве аргумента функцию-колбэк, которая выполняется сразу после того, как промис поменял состояние на rejected или fulfilled :

Цепочки методов

Методы then ( ) , catch ( ) и finally ( ) часто объединяют в цепочки вызовов, чтобы обработать и успешный, и ошибочный сценарии:

В этом случае при успешном завершении операции мы выполним код из then ( ) , при ошибке — код из catch ( ) . Затем выполнится код из finally ( ) .

Цепочки методов — очень гибкий подход. Он позволяет создавать зависимые асинхронные операции.

Например, нужно отобразить информацию о фильме и главном герое. Мы не знаем, кто главный герой, не получив эту информацию из данных о фильме. Таким образом, запрос данных о герое зависит от результата запроса данных о фильме.

Промисы делают решение простым и читаемым. Мы можем начинать следующее асинхронное действие внутри колбэка метода then ( ) . Все, что возвращается из колбэка, оборачивается в промис, поэтому в цепочку можно добавить новый then ( ) :

Обработка ошибок в цепочках методов

Цепочки then ( ) при обработке промисов могут быть очень большими. В примере выше цепочка состоит из четырёх then ( ) и одного catch ( ) . Как в этом случае отработает catch ( ) ?

☝️ catch ( ) обрабатывает ошибки от всех then ( ) между ним и предыдущим catch ( ) .

В примере выше наш catch ( ) — последний, а предыдущего нет, поэтому он будет обрабатывать все ошибки.

Если в цепочке несколько catch ( ) , то каждый ловит ошибки от then ( ) , находящихся выше.

Возможен вариант, когда финального catch ( ) нет. Тогда ошибки от последних then ( ) не будут обрабатываться.

⚠️ Такой код — плохой. Если в одном из последних then ( ) произойдёт ошибка, то вся дальнейшая цепочка не отработает, при этом, из-за асинхронной природы промиса, прочий код вне промиса продолжит работать и приложение не упадёт.

Как создать асинхронную функцию с промисом

  1. Создать функцию, которая будет выполнять асинхронную операцию:
  1. Вернуть из функции свежесозданный промис:
  1. Аргументом в конструктор передать функцию, которая выполняет асинхронную операцию и переводит промис в состояние «успех» или «ошибка» в зависимости от результата:

💡 Если асинхронная операция работает через колбэки, то её стоит обернуть в промис, чтобы писать более читаемый код.

Функция get Data в примере ниже принимает два аргумента — колбэк при успехе и колбэк при ошибке. Завернём её в промис:

Теперь можно использовать методы then ( ) и catch ( ) :

На практике

Николай Лопин советует

🛠 Промис становится «разрешённым» или «завершённым», когда он переходит из состояние pending в fulfilled или rejected . Состояние завершённого промиса нельзя поменять.

🛠 Всегда завершайте использование промиса методом catch ( ) . Если этого не сделать, то следующие промисы в цепочке перестанут работать, и такую ошибку получится поймать только через специальный обработчик – unhandledrejection .

🛠 Время от времени нужно выполнить несколько асинхронных функций и дождаться, пока все выполнятся или одна из них завершится ошибкой. Для этого существует статический метод Promise . all ( ) (она возвращает промис).

🛠 Если нужно дождаться пока несколько асинхронных функций завершатся (без разницы, успешно или ошибкой), используйте метод Promise . all Settled ( ) (вернёт промис).

На собеседовании

Это партнёрская рубрика, мы выпускаем её совместно с сервисом онлайн-образования Яндекс Практикум. Приносите вопрос, на который не знаете ответа, в задачи, мы разложим всё по полочкам и опубликуем. Если знаете ответ, присылайте пулреквест на GitHub.

Реализуйте полифил для Promise.all

Это вопрос без ответа. Вы можете помочь! Почитайте о том, как контрибьютить в Доку.

Читайте также

Что такое API

Подгрузка контента при прокрутке

async / await

Ждать выполнения асинхронного кода стало легче! Больше никаких колбэков и промисов, только новые ключевые слова.

Источник статьи: http://doka.guide/js/promise/

JavaScript Promise с примерами

В этой статье разберём что такое промисы, как они работают, зачем они нужны, а также различные примеры их использования.

Что такое promise?

По умолчанию код в JavaScript выполняется последовательно (в одном потоке, синхронно). То есть таким образом, когда каждая следующая операция ждёт завершения предыдущей.

На сайте очень часто встречаются задачи, для выполнения которых требуется значительное время. Если их реализовать с помощью синхронного кода, то это может привести к тому, что страницы будут подвисать (т.е. с ними нельзя будет взаимодействовать некоторое время). Таким образом, возможно возникновение негативного пользовательского опыта при использовании такого сайта и таких сценариев конечно лучше не допускать.

Чтобы этого избежать необходимо использовать асинхронный код. Он в отличие от синхронного выполняется в фоновом режиме и не блокирует основной поток. Т.е. код, расположенный после него выполняется сразу же, не дожидаясь его завершения.

Асинхронный код в JavaScript может быть написан разными способами : с помощью обратных вызовов, promise (обещаний) и ключевых слов async/await .

В этой статье рассмотрим, как это сделать с помощью promise .

Promise (промисы, обещания) – это специальный объект и набор методов в JavaScript для удобного написания асинхронного кода.

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

Изучение промисов начнём с рассмотрения примера из реальной жизни .

Допустим, папа обещает дать 100$, если вы сдадите завтрашний экзамен на хорошо или отлично.

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

Но как только вы получите отметку по экзамену, обещание завершится. Далее в зависимости от того успешно оно завершилось или нет будет зависеть получите ли вы 100$ или нет.

На JavaScript этот пример с помощью promise реализуется следующим образом:

А теперь разберём как всё это работает, и начнём с создания промиса.

Создание промиса

Начинается процесс написания промиса с его создания. Осуществляется это с помощью конструктора, т.е. с new Promise() :

Конструктор промиса принимает 2 аргумента, которые являются функциями. Первый аргумент обычно называют resolve , а второй – reject . Внутрь промиса помещают асинхронный код, можно конечно и синхронный, но тогда в этом не будет смысла.

Промис завершает своё выполнение, когда вызывается функция resolve() или reject() .

Функцию resolve() вызывают обычно в том месте кода, в котором асинхронная операция должна завершиться успешно. А функцию reject() – там, где она должна завершиться с ошибкой.

Состояния, в которых может находиться промис

Промис начинается с состояния ожидания ( state: «pending» ). Оно говорит о том, что он ещё не выполнен (результат undefined ).

Промис завершается после вызова resolve() или reject() . При этом его состояние переходит соответственно в выполнено ( state: «fulfilled» ) или отклонено ( state: «rejected» ).

Внутрь функций resolve() или reject() можно поместить аргумент, который затем будет доступен соответственно в then() или catch() .

Когда passexam равен true , промис успешно завершится через 5 секунд посредством вызова функции resolve() . А так как промис завершился успешно, то будет вызван метод then() .

Если значение константы passexam поменять на false , то промис завершится с ошибкой через 5 секунд с помощью вызова функции reject() . А так как в этом случае промис завершился с ошибкой, то, следовательно, будет вызван catch() .

Методы промисов

У каждого промиса есть определённый набор методов, которые мы можем использовать:

  • then – выполняется, когда промис завершился успешно (после вызова функции resolve() );
  • catch – вызывается, если промис завершается ошибкой (после вызова reject() );
  • finally – выполняется в любом случае после завершения промиса, вне зависимости от конечного состояния.

Пример с использованием всех трёх методов:

При этом then() позволяет обработать не только успех, но и ошибку. Для этого необходимо передать функцию в качестве второго аргумента.

Второй аргумент указывать не обязательно, в этом случае он будет реагировать только на успех:

Для того чтобы получить значение из промиса в методах then() и catch() как уже было отмечено выше необходимо его передать в функции resolve() и reject() .

Цепочка промисов

Цепочка промисов – это простая концепция, в основу которой положено то, что методы промисов тоже в качестве результата возвращают промис. А так как они возвращают промис, то мы для них в свою очередь тоже можем вызвать методы и т.д. Таким образом, мы можем создавать цепочку из промисов, каждый из которых выполняется только после того как завершился предыдущий.

По простому цепочка промисов позволяет очень компактно записать код для последовательного выполнения нескольких асинхронных задач, каждая из которых должна выполняется после завершения предыдущей.

  • сначала выполняется первый промис, который успешно завершится через 3 секунды со значением 2;
  • после этого выполнится метод then() , который выведет в консоль значение переменной value , которое равно аргументу, переданному в resolve() и возвратит в качестве результата новый промис; он также как и предыдущий успешно завершится через 3 секунды со значением value * 2 ;
  • затем выполнится следующий then() , он выполнит действия аналогично предыдущему then() ;
  • дальше выполнится последний then() , который просто выведет в консоль значение параметра value .

Promice.all() и Promise.race()

Promice.all() и Promise.race() – это статические методы Promice , которые принимают на вход массив промисов и возвращают новый промис.

В случае с Promice.all() промис завершится когда завершатся все промисы в массиве.

При использовании Promise.race() промис завершится, как только выполнится первый промис из массива.

Задачи

1. Асинхронный код, ниже реализованный с использованием функций обратного вызова, необходимо переписать через промисы:

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

3. Перепишите код для динамической загрузки скриптов в определенном порядке через промисы:

Источник статьи: http://itchief.ru/javascript/promise

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *