Javascript метод promise.all()

Promises States In Javascript

The Promise is the proxy for the value not necessarily known when a promise is created. The promise allows you to associate the handlers with an asynchronous action’s eventual success or failure reason.

This lets asynchronous methods return the values like synchronous methods: instead of immediately returning a final value, an asynchronous method returns a promise to supply value at some point in the future.

The Promise is in one of these states:

  1. Pending: an initial state or pending state, neither fulfilled nor rejected.
  2. Fulfilled: meaning that the operation completed successfully.
  3. Rejected: meaning that the operation failed.

Выполнение промиса

При запуске мы выполняем промис и ожидаем успешного выполнения, используя обратный вызов .
Так как мы создали промис, а затем сразу же выполнили — передав Alex is cool, мы сразу увидем результат в консоле.

Если нужно сделать через некоторое время

Если мы хотим выполнить некоторую обработку в фоновом режиме или сделать AJAX-запрос, а затем, когда данные вернутся, сделать . По сути, всё сводится к тому, что «я не хочу останавливать выполнение JavaScript, я просто хочу начать запрос, а затем, когда он вернется, разберусь с этим результатом».

Давай посмотрим, что произойдет, когда мы установим здесь тайм-аут в 1 секунду.

Через секунду в консоле появится Alex is cool. Точно так же мы можем вызвать :

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

Теперь в консоле браузера через 1 секунду будет только реальная ошибка, которую мы передали. В идеале, для более детальной информации, где произошла ошибка, мы должны передать объект , а не просто строку. Поэтому мы обернем строку в объект , а затем, когда ошибка выведется в консоль, мы увидим, что у нас есть информация о том, где она произошла.

Promise.any

Similar to , but waits only for the first fulfilled promise and gets its result. If all of the given promises are rejected, then the returned promise is rejected with – a special error object that stores all promise errors in its property.

The syntax is:

For instance, here the result will be :

The first promise here was fastest, but it was rejected, so the second promise became the result. After the first fulfilled promise “wins the race”, all further results are ignored.

Here’s an example when all promises fail:

As you can see, error objects for failed promises are available in the property of the object.

Awaiting a promise

In order to use a promise, we must somehow be able to wait for it
to be fulfilled or rejected. The way to do this is using
(see warning at the end of this section if
attempting to run these samples).

With this in mind, it’s easy to re-write our earlier
function to use promises:

This still has lots of error handling code (we’ll see how we can
improve on that in the next section) but it’s a lot less error
prone to write, and we no longer have a strange extra parameter.

Non Standard

Note that (used in the examples in
this section) has not been standardised. It is supported by most
major promise libraries though, and is useful both as a teaching
aid and in production code. I recommend using it along with the following polyfill (minified / unminified):

Promise.all

Допустим, нам нужно запустить множество промисов параллельно и дождаться, пока все они выполнятся.

Например, параллельно загрузить несколько файлов и обработать результат, когда он готов.

Для этого как раз и пригодится .

Синтаксис:

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

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

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

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

Часто применяемый трюк – пропустить массив данных через map-функцию, которая для каждого элемента создаст задачу-промис, и затем обернёт получившийся массив в .

Например, если у нас есть массив ссылок, то мы можем загрузить их вот так:

А вот пример побольше, с получением информации о пользователях GitHub по их логинам из массива (мы могли бы получать массив товаров по их идентификаторам, логика та же):

Если любой из промисов завершится с ошибкой, то промис, возвращённый , немедленно завершается с этой ошибкой.

Например:

Здесь второй промис завершится с ошибкой через 2 секунды. Это приведёт к немедленной ошибке в , так что выполнится : ошибка этого промиса становится ошибкой всего .

В случае ошибки, остальные результаты игнорируются

Если один промис завершается с ошибкой, то весь завершается с ней, полностью забывая про остальные промисы в списке. Их результаты игнорируются.

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

ничего не делает для их отмены, так как в промисах вообще нет концепции «отмены». В главе Fetch: прерывание запроса мы рассмотрим , который помогает с этим, но он не является частью Promise API.

разрешает передавать не-промисы в (перебираемом объекте)

Обычно, принимает перебираемый объект промисов (чаще всего массив). Но если любой из этих объектов не является промисом, он передаётся в итоговый массив «как есть».

Например, здесь результат:

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

AJAX Functions Included

Because AJAX requests are the root of much asynchrony in Javascript, promise.js provides the following functions:

promise.get(url, data, headers)
promise.post(url, data, headers)
promise.put(url, data, headers)
promise.del(url, data, headers)

(optional) : a {key: value} object or url-encoded string.

(optional) : a {key: value} object (e.g. ).

Example:

promise.get('/').then(function(error, text, xhr) {
    if (error) {
        alert('Error ' + xhr.status);
        return;
    }
    alert('The page contains ' + text.length + ' character(s).');
});

You can set a time in milliseconds after which unresponsive AJAX
requests should be aborted. This is a global configuration option,
disabled by default.

Промисы в ES5, ES6/2015, ES7/Next

ES 5 — поддерживают почти все браузеры. Демо код работает в ES5 среде (Все основные браузеры + NodeJS), если бы вы подключили библиотеку промисов Bluebird. Почему так? Потому что ES5 не поддерживает промисы из коробки. Другая знаменитая библиотека промисов это Q, от Криса Коваля.

ES6 / ES2015 — демо код сработает прямо из коробки, так как ES6 поддерживает промисы естественным путём. Более того, с ES6 функциями, мы можем ещё круче упростить код с помощью  и использовать const и .

Обратите внимание, что все  заменены на . Все  были упрощены на  

ES7 —  делают синтаксис визуально лучше. ES7 представил async и await синтаксис. Это делает асинхронный синтаксис визуально лучше и проще для понимания, без  и  . Перепишем свой пример с ES7 синтаксисом.

1. Всегда, когда вам нужно возвратить промис в функцию, вы ставите спереди  к этой функции. Для примера, 

2. Всякий раз, когда вам надо вызвать промис, вам надо вставить . Для примера,  и 

3. Используйте   , чтобы словить ошибку промиса, отклоненную промисом.

Нормальная функция против асинхронной.

Давайте посмотрим на эти два примера, каждый пример делает сложение двух чисел, один пример с нормальной функцией, другой с удаленной.

Нормальная функция для сложения чисел.

Асинхронная функция для сложения двух чисел:

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

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

Вызов API, скачивание файлов, чтение файлов — всё это те обычные async операции, которые вы можете выполнять.

Концепция 2: Цепочка промисов

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

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

Цепочка вызовов может быть реализована двумя способами:

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

timer().then(successHandler1, failureHandler1).
.then(successHandler2, failureHandler2)
.then(successHandler3, failuerHandler3)
.then(successHandler4, failureHandler4)

Использование стандартного обработчика ошибок – обратный вызов failureHandlerне является обязательным в методе then. Поэтому можно использовать блок catch для общей обработки ошибок:

timer().then(successHandler1)
.then(successHandler2)
.then(successHandler3)
.catch(errorHandler)

В приведенном выше примере можно передать параметр через разные обратные вызовы и увеличивать их значение.

Если мы возвращаем error из любого обратного вызова then, то для последующих методов then обратный вызов error будет выполнен. После чего ошибка будет передана в последний блок catch.

Bigger example: fetch

In frontend programming promises are often used for network requests. So let’s see an extended example of that.

We’ll use the fetch method to load the information about the user from the remote server. It has a lot of optional parameters covered in separate chapters, but the basic syntax is quite simple:

This makes a network request to the and returns a promise. The promise resolves with a object when the remote server responds with headers, but before the full response is downloaded.

To read the full response, we should call the method : it returns a promise that resolves when the full text is downloaded from the remote server, with that text as a result.

The code below makes a request to and loads its text from the server:

The object returned from also includes the method that reads the remote data and parses it as JSON. In our case that’s even more convenient, so let’s switch to it.

We’ll also use arrow functions for brevity:

Now let’s do something with the loaded user.

For instance, we can make one more request to GitHub, load the user profile and show the avatar:

The code works; see comments about the details. However, there’s a potential problem in it, a typical error for those who begin to use promises.

Look at the line : how can we do something after the avatar has finished showing and gets removed? For instance, we’d like to show a form for editing that user or something else. As of now, there’s no way.

To make the chain extendable, we need to return a promise that resolves when the avatar finishes showing.

Like this:

That is, the handler in line now returns , that becomes settled only after the call of in . The next in the chain will wait for that.

As a good practice, an asynchronous action should always return a promise. That makes it possible to plan actions after it; even if we don’t plan to extend the chain now, we may need it later.

Finally, we can split the code into reusable functions:

Promise Methods

The Javascript Promise has the following methods.

It returns the promise that either fulfills when all of the promises in the iterable argument have fulfilled or rejects as soon as one of the promises in the iterable argument rejects the promise.
If the returned promise fulfills, it is fulfilled with an array of the values from the fulfilled promises in the same order as defined in the iterable. If the returned promise rejects, it is rejected with the reason from the first promise in the iterable that rejected.

It returns the promise that fulfills or rejects a.s.a. one of the promises in the iterable fulfills or rejects, with a value or reason from that promise.

Returns the Promise object that is rejected for the given reason.
It returns the Promise object that is resolved with the given value.

JavaScript Promise Object

A JavaScript Promise object contains both the producing code and calls to the consuming code:

Promise Syntax

let myPromise = new Promise(function(myResolve, myReject) {
// «Producing Code» (May take some time)
  myResolve(); // when successful
  myReject();  // when error
});
// «Consuming Code» (Must wait for a fulfilled Promise)
myPromise.then(
  function(value) { /* code if successful */ },
  function(error) { /* code if some error */ }
);

When the executing code obtains the result, it should call one of the two callbacks:

Result Call
Success myResolve(result value)
Error myReject(error object)

Consuming a Promise

So, if we resolve the function, then we get the value inside then() block. Let us take the following example and see the output inside the terminal.

// server.js

const prom = new Promise(function(resolve, reject) {
  setTimeout(() => {
    resolve('Promise is created and consumed');
  }, 200);
});

prom.then(value => {
  console.log(value);
});

Now, run the above file in node.js by typing the following command.

The promise is also rejected, and in that scenario, our code looks like the below snippet.

// server.js

const prom = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('Promise is rejected');
  }, 200);
});

prom.catch(error => {
  console.log(error);
});

Output

So understanding promises are not that difficult if an excellent example is provided.

The executor initiates some asynchronous work typically, and then, once that completes, either call the resolve function to resolve a promise or else rejects it if an error occurred.

If the error is thrown in the executor function, it means that the promise is rejected. The return value of the executor is ignored.

Применяем промисы

Теперь у нас есть промис, давайте применим его:

1. Мы вызываем функцию в . В этой функции, мы применим наш промис .

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

3. В нашем примере, у нас в . Какое значение у ?  значение это точное значение в вашем промисе (значение при успехе). Следовательно, это будет .

4. У нас есть  в . Какое значение будет у ? Как вы могли предположить,  значение именно то, которое вы указали в промисе (значение при неудаче). Следовательно, в этом случае это будет .

Давайте запустим этот пример и увидим результат!

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

Да, в промисах есть цепочки.

Давайте представим, что вы ребенок и обещали своему другу, что покажете ему новый телефон, когда вам его купят. Это будет ещё один промис.

В этом примере вы уже наверное поняли, что мы не вызывали . Так как, в принципе, это опционально.

Мы вообще можем сократить этот пример, используя .

А теперь давайте свяжем наши промисы. Вы — ребенок и можете запустить  промис только после промиса .

Вот так легко связывать промисы.

Пример использования

Базовое использования метода

const promise = new Promise(function(resolve, reject) {
    setTimeout(resolve, 2000, "promise1"); // изменяем состояние объекта на fulfilled (успешное выполнение) через 2 секунды
});

const promise2 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 1000, "promise2"); // изменяем состояние объекта на fulfilled (успешное выполнение) через 1 секунду
});

const promise3 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 500, "promise3"); // изменяем состояние объекта на fulfilled (успешное выполнение) через пол секунды
});

Promise.all([promise, promise2, promise3]) // ожидаем выполнение всех переданных в массиве обещаний / или отклонения какого-то обещания
                .then(val => console.log(val)); // обработчик для успешного выполнения

// 

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

С использованием метода .all() объекта Promise мы принимаем массив, который содержит ссылку на три обещания, результат выполнения этого метода зависит от того с каким результатом завершится выполнение обещаний, в нашем случае он возвращает объект Promise, который имеет состояние fulfilled (успешное выполнение), так как все переданные объекты Promise в аргументе имеют состояние fulfilled (успешное выполнение).

С использованием метода then() мы добавили обработчик, вызываемый когда объект Promise имеет состояние fulfilled (успешное выполнение), и выводим в консоль полученное значение (массив полученных значений из всех обещаний).

Далее мы с Вами рассмотрим пример в котором увидим, что произойдет, если одно из обещаний будет отклонено:

const promise = new Promise(function(resolve, reject) {
    setTimeout(resolve, 2000, "promise1"); // изменяем состояние объекта на fulfilled (успешное выполнение) через 2 секунды
});

const promise2 = new Promise(function(resolve, reject) {
    setTimeout(reject, 1000, new Error("Обещание отклонено")); // изменяем состояние объекта на rejected (выполнение отклонено) через 1 секунду
});

const promise3 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 500, "promise3"); // изменяем состояние объекта на fulfilled (успешное выполнение) через пол секунды
});

Promise.all([promise, promise2, promise3]) // ожидаем выполнение всех переданных в массиве обещаний / или отклонения какого-то обещания
                .then(val => console.log(val), // обработчик для успешного выполнения
                           err => console.log(err.message)); // обработчик для случая, когда выполнение отклонено

// Обещание отклонено

По аналогии с предыдущим примером мы инициализировали три переменные, которые содержат объект Promise, который изменяет состояние объекта на fulfilled (успешное выполнение) через 2 секунды в первой и через пол секунды в третьей переменной

Обратите внимание, что с помощью метода reject() мы изменяем значение объекта Promise на rejected (выполнение отклонено) через 1 секунду во второй переменной

С использованием метода .all() объекта Promise мы принимаем массив, который содержит ссылку на три обещания, результат выполнения этого метода зависит от того с каким результатом завершится выполнение обещаний, в нашем случае он возвращает объект Promise, который имеет состояние rejected (выполнение отклонено), это связано с тем, что один из переданных объектов изменил своё состояние на rejected (выполнение отклонено).

С использованием метода then() мы добавили обработчики, вызываемые когда объект Promise имеет состояние fulfilled (успешное выполнение), или rejected (выполнение отклонено). В нашем случае срабатывает обработчик для отклоненного выполнения, и выводит информацию об ошибке в консоль.

Нюансы использования метода

Promise.all()
                .then(val => console.log(val)); // обработчик для успешного выполнения

// 

Promise.all()
                .then(val => console.log(val)); // обработчик для успешного выполнения

// 

Promise.all([])
                .then(val => console.log(val)); // обработчик для успешного выполнения

// []            

В этом примере мы рассмотрели основные нюансы использования метода .all(), например, если объект содержит одно, или несколько значений, которые не являются обещаниями, то метод разрешится с этими значениями. Если переданный объект пуст, то возвращенное обещание будет сразу переведено в состояние fulfilled (успешное выполнение).

JavaScript Promise

Promise reaction как последствие исполненного обещания

Как вы могли заметить на рисунке 14, последствия разрешения / резолвинга promise объекта подписаны как «+реакция» и «-реакция». Официальный термин для этих слов из ECMAScript спецификации — promise reaction. Предполагается, что в следующих статьях эта тема будет рассмотрена подробно. Пока что ограничимся общим представлением того, что такое promise reaction, чтобы можно было правильно ассоциировать этот термин с философским смыслом этого слова и его техническим исполнением.

Как мы помним, у обещания могут быть последствия, а могут и не быть. Что же такое последствие? Это действие, которое произойдет некоторым временем позже: после того как обещание исполнится. А раз это действие, то последствие может быть выражено обычной JavaScript функцией. Одни функции исполнятся в случае успешного резолвинга промиса (+реакция); другие функции — в случае, когда промис перейдет в состояние rejected (-реакция). Технически эти функции (последствия) передаются аргументами при вызове метода Promise.prototype.then().

Таким образом важной частью promise reaction является асинхронное действие, выполняемое когда-то в будущем. Есть и вторая важная составляющая часть promise reaction — это новосозданный промис, возвращаемый после выполнения команды Promise.prototype.then()

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

По факту promise reaction связывает обещания между собой в каком-то временном интервале

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

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

рис 18. ( Последствия разрешения обещания записываются callback функциями в методе then(). Callback будет вызван асинхронно автоматически движком JS )

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

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

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

Да мало ли, где ошибка…

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

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

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

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

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

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

А что после ?

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

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

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

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

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

Promise.allSettled

A recent addition

This is a recent addition to the language.
Old browsers may need polyfills.

rejects as a whole if any promise rejects. That’s good for “all or nothing” cases, when we need all results successful to proceed:

just waits for all promises to settle, regardless of the result. The resulting array has:

  • for successful responses,
  • for errors.

For example, we’d like to fetch the information about multiple users. Even if one request fails, we’re still interested in the others.

Let’s use :

The in the line above will be:

So for each promise we get its status and .

If the browser doesn’t support , it’s easy to polyfill:

In this code, takes input values, turns them into promises (just in case a non-promise was passed) with , and then adds handler to every one.

That handler turns a successful result into , and an error into . That’s exactly the format of .

Now we can use to get the results of all given promises, even if some of them reject.

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

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

Adblock
detector