Главная » Хабрахабр » [Перевод] Использование промисов в JavaScript

[Перевод] Использование промисов в JavaScript

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

Вот некоторые из них

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

Её автор говорит, что он последние 10 лет занимался разработкой на Java и PHP, но всё это время с интересом поглядывал на JavaScript. Поэтому, если вы хотите лучше понять промисы, мы предлагаем вашему вниманию перевод очередной статьи, посвящённой этой теме. Недавно он решил всерьёз заняться JS и первой заинтересовавшей его темой стали промисы.

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

JavaScript глазами новичка

Тот, кто начинает писать на JavaScript, может почувствовать себя, что называется, «не в своей тарелке». Одни говорят, что JS — это синхронный язык программирования, другие утверждают, что он — асинхронный. Новичок слышит о коде, который блокирует главный поток, и о коде, который его не блокирует, о паттернах проектирования, основанных на событиях, о жизненном цикле событий, о стеке вызовов функций, об очереди событий и об их всплытии, о полифиллах. Он узнаёт, что существуют такие штуки, как Babel, Angular, React, Vue и мириады других библиотек. Если вы только что узнали в таком вот «новичке» себя — не стоит из-за этого волноваться. Вы — не первый и не последний. Есть даже термин для этого — так называемая «JavaScript-усталость» (JavaScript fatigue). По этому поводу метко высказался Лукас Ф Коста: «JavaScript-усталость — это то, что можно наблюдать тогда, когда люди используют инструменты, которые им не нужны, для решения проблем, которых у них нет».

Итак, JavaScript — это синхронный язык программирования, который, благодаря механизму коллбэков, позволяет вызывать функции так, как это делается в асинхронных языках. Но не будем о грустном.

Простой рассказ о промисах

Слово «promise» переводится как «обещание». Promise-объекты в программировании, которые мы называем «промисами», очень похожи на обычные обещания, которые люди дают друг другу в реальной жизни. Поэтому давайте сначала поговорим о таких вот обещаниях.

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

Итак, что мы знаем об обещаниях?

  1. Обещание даёт вам гарантию того, что что-либо будет сделано. При этом не имеет значения, кто именно это сделает: тот, кто дал обещание, или кто-то другой, по просьбе того, кто дал обещание. Обещание даёт уверенность в чём-то, основываясь на этой уверенности тот, кто получил обещание, может, например, строить какие-то планы.
  2. Обещание может быть либо выполнено, либо нет.
  3. Если обещание будет выполнено, то вы, в результате, ожидаете чего-либо, что вы сможете использовать в дальнейшем для выполнения каких-либо действий или реализации каких-то планов.
  4. Если обещание оказывается невыполненным, то вам захочется узнать, почему тот, кто его дал, не смог его выполнить. После того, как вы узнаете причину произошедшего и у вас будет уверенность в том, что обещание не выполнено, вы можете размышлять о том, что делать дальше, или о том, как справиться с возникшей ситуацией.
  5. После того, как вам что-то пообещали, всё, что у вас есть — это некая гарантия. Вы не можете воспользоваться тем, что вам обещано, немедленно. Вы можете определить для себя — что понадобится сделать в том случае, если обещание будет выполнено (следовательно, вы получите обещанное), и что понадобится сделать, если оно окажется нарушенным (в таком случае вы знаете причину произошедшего, а следовательно можете продумать запасной план действий).
  6. Есть вероятность, что человек, давший обещание, попросту исчезнет. В подобных случаях полезно, чтобы обещание было бы привязано к каким-то временным рамкам. Например, если тот, кто дал вам обещание, не объявится через 10 дней, можно считать, что у него возникли какие-то проблемы и обещание он нарушил. В результате, даже если тот, кто дал обещание, выполнит его через 15 дней, это не будет иметь значения, так вы уже действуете по альтернативному плану, не полагаясь на обещание.

Теперь переходим к JavaScript.

Промисы в JavaScript

У меня есть одно правило: занимаясь JavaScript, я всегда читаю документацию, которую можно найти на MDN. Мне кажется, что этот ресурс выгодно отличается от остальных конкретностью и чёткостью изложения. Поэтому, изучая промисы, я ознакомился с соответствующим материалом и поэкспериментировал с кодом для того, чтобы привыкнуть к новым для себя синтаксическим конструкциям.

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

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

Вот как создают промисы:

new Promise( /* executor */ function(resolve, reject) );

Конструктор принимает функцию, выполняющую некие действия, мы назвали её здесь executor. Эта функция принимает два параметра — resolve и reject, которые, в свою очередь, также являются функциями. Промисы обычно используются для выполнения асинхронных операций или кода, который может заблокировать главный поток, например — такого, который работает с файлами, выполняет вызовы неких API, делает запросы к базам данных, занимается операциями ввода-вывода, и так далее. Запуск подобных асинхронных операций выполняется в функции executor. Если асинхронная операция будет завершена успешно, тогда результат, ожидаемый от промиса, будет возвращён путём вызова функции resolve. Ситуация, в которой вызывается эта функция, определяется создателем промиса. Аналогично, при возникновении ошибки, сведения о том, что случилось, возвращают, вызывая функцию reject.

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

var keepsHisWord;
keepsHisWord = true;
promise1 = new Promise(function(resolve, reject) { if (keepsHisWord) { resolve("The man likes to keep his word"); } else { reject("The man doesnt want to keep his word"); }
});
console.log(promise1);

Вот что выведет этот код:

У промиса есть состояние (PromiseStatus) и значение (PromiseValue)

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

promise2 = new Promise(function(resolve, reject) { setTimeout(function() { resolve({ message: "The man likes to keep his word", code: "aManKeepsHisWord" }); }, 10 * 1000);
});
console.log(promise2);

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

Неразрешённый промис

В результате и PromiseStatus, и PromiseValue будут соответствующим образом обновлены. После того, как пройдут 10 секунд, промис будет разрешён. Сделано это для того, чтобы продемонстрировать возможность возврата с помощью функции resolve сложных структур данных. Как видите, в этом примере мы изменили функцию, вызываемую при успешном разрешении промиса, теперь она возвращает не обычную строку, а объект.

Промис, разрешённый через 10 секунд и возвращающий объект

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

keepsHisWord = false;
promise3 = new Promise(function(resolve, reject) { if (keepsHisWord) { resolve("The man likes to keep his word"); } else { reject("The man doesn't want to keep his word"); }
});
console.log(promise3);

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

Отклонённый промис

Когда промис создаётся, в PromiseStatus будет значение pending, а в PromiseValue будет undefined. Теперь, проанализировав все три примера, мы можем видеть, что в PromiseStatus могут появляться три разных значения: pending (ожидание), resolved (успешное разрешение) и rejected (отклонение). Когда промис находится в состоянии resolved или rejected, его называют заданным (settled) промисом. Эти значения будут сохраняться до разрешения или отклонения промиса. Такой промис перешёл из состояния ожидания в состояние, в котором он имеет либо состояние resolved, либо состояние rejected.

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

Объект Promise

В соответствии с документацией MDN, объект Promise представляет собой результат успешного или неудачного завершения асинхронной операции.

Статические методы можно вызывать, не создавая экземпляр объекта, а для вызова методов прототипа нужен экземпляр объекта Promise. У объекта Promise есть статические методы и методы прототипа объекта. Это упрощает работу. Учитывайте, что и статические и обычные методы возвращают объекты Promise.

▍Методы прототипа объекта Promise

Поговорим сначала о методах прототипа объекта Promise. Существует три таких метода. Не забывайте о том, что эти методы можно вызывать у экземпляра объекта Promise, и то, что они сами возвращают промисы. Благодаря всем этим методам можно назначать обработчики, реагирующие на изменение состояния промисов. Как мы уже видели, когда промис создаётся, он находится в состоянии pending. При переходе промиса в состояние resolved или rejected будет вызван, как минимум, один из следующих методов:

Promise.prototype.catch(onRejected)
Promise.prototype.then(onFulfilled, onRejected)
Promise.prototype.finally(onFinally)

Ниже показана схема работы промиса и события, приводящие к вызову методов .then и .catch. Так как эти методы возвращают объекты Promise, их вызовы можно объединять в цепочки, это также отражено на схеме. Если в промисе предусмотрено использование метода .finally, он будет вызван тогда, когда промис перейдёт в состояние settled, независимо от того, был ли этот промис успешно разрешён или отклонён.


Схема работы промиса (изображение взято отсюда)

Вы — школьник и просите, чтобы ваша мама купила вам мобильник. Вот небольшая история. Теперь перескажем эту историю языком JavaScript. Она говорит: «Если наши сбережения будут больше, чем стоит телефон, я его тебе куплю».

var momsPromise = new Promise(function(resolve, reject) { momsSavings = 20000; priceOfPhone = 60000; if (momsSavings > priceOfPhone) { resolve({ brand: "iphone", model: "6s" }); } else { reject("We donot have enough savings. Let us save some more money."); }
});
momsPromise.then(function(value) { console.log("Hurray I got this phone as a gift ", JSON.stringify(value));
});
momsPromise.catch(function(reason) { console.log("Mom coudn't buy me the phone because ", reason);
});
momsPromise.finally(function() { console.log( "Irrespecitve of whether my mom can buy me a phone or not, I still love her" );
});

Вот что выведет этот код:

Мама не сдержала обещание

В таком случае вышеприведённый код выведет следующее. Если же мы изменим значение переменной momsSavings на 200000, тогда мама сможет купить сыну подарок.

Мама выполнила обещание

Поговорим об эффективном использовании методов .then и .catch. Теперь давайте представим, что рассматриваемый код оформлен в виде библиотеки, а мы этой библиотекой пользуемся.

Вот как это может выглядеть: Так как методу .then можно назначать и обработчик onFulfilled, вызываемый при успешном разрешении промиса, и обработчик onRejected, вызываемый при отклонении промиса, вместо того, чтобы использовать и метод .then и метод .catch, мы можем добиться того же эффекта с помощью одного лишь метода .then.

momsPromise.then( function(value) { console.log("Hurray I got this phone as a gift ", JSON.stringify(value)); }, function(reason) { console.log("Mom coudn't buy me the phone because ", reason); }
);

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

Для того чтобы лучше понять то, что мы рассмотрим далее, давайте создадим функцию, которая возвращает промис, разрешение или отклонение которого происходит случайным образом. Для того чтобы эти примеры можно было бы запускать в браузере, а конкретно, в Google Chrome, я постарался, чтобы тут не было бы внешних зависимостей. Для того чтобы разобраться в особенностях работы асинхронных функций, будем задавать в наших промисах случайные задержки. Это позволит нам испытать различные сценарии работы с промисами. Вот эта функция. Так как нам нужны случайные числа, создадим функцию, которая возвращает случайное число между x и y.

function getRandomNumber(start = 1, end = 10) { //предполагается, что при использовании этой функции start и end >=1 и end > start return parseInt(Math.random() * end) % (end-start+1) + start;
}

Теперь создадим функцию, которая возвращает промисы. Назовём её promiseTRRARNOSG. Название этой функции расшифровывается как promiseThatResolvesRandomlyAfterRandomNumnberOfSecondsGenerator, то есть это — генератор промисов, которые случайным образом разрешаются или отклоняются через случайное число секунд. Эта функция будет создавать промис, который будет разрешён или отклонён через случайный промежуток времени между 2 и 10 секундами. Для того чтобы случайным образом разрешать или отклонять промис, мы получаем случайное число между 1 и 10. Если это число больше 5 — промис будет разрешён, если нет — отклонён.

function getRandomNumber(start = 1, end = 10) { //предполагается, что при использовании этой функции start и end >=1 и end > start return (parseInt(Math.random() * end) % (end - start + 1)) + start;
}
var promiseTRRARNOSG = (promiseThatResolvesRandomlyAfterRandomNumnberOfSecondsGenerator = function() { return new Promise(function(resolve, reject) { let randomNumberOfSeconds = getRandomNumber(2, 10); setTimeout(function() { let randomiseResolving = getRandomNumber(1, 10); if (randomiseResolving > 5) { resolve({ randomNumberOfSeconds: randomNumberOfSeconds, randomiseResolving: randomiseResolving }); } else { reject({ randomNumberOfSeconds: randomNumberOfSeconds, randomiseResolving: randomiseResolving }); } }, randomNumberOfSeconds * 1000); });
});
var testProimse = promiseTRRARNOSG();
testProimse.then(function(value) { console.log("Value when promise is resolved : ", value);
});
testProimse.catch(function(reason) { console.log("Reason when promise is rejected : ", reason);
});
// С помощью цикла создадим десять разных промисов с использованием нашей функции для того, чтобы увидеть разные промисы. Некоторые из них будут разрешены, некоторые - отклонены. for (i=1; i<=10; i++) { let promise = promiseTRRARNOSG(); promise.then(function(value) { console.log("Value when promise is resolved : ", value); }); promise.catch(function(reason) { console.log("Reason when promise is rejected : ", reason); });
}

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

▍Статические методы объекта Promise

Существует четыре статических методы объекта Promise.

Вот два метода — Promise.reject(reason)и Promise.resolve(value), которые позволяют создавать, соответственно, отклонённые и разрешённые промисы.

Вот как работать с методом Promise.reject, создающим отклонённые промисы.

var promise3 = Promise.reject("Not interested");
promise3.then(function(value){ console.log("This will not run as it is a resolved promise. The resolved value is ", value);
});
promise3.catch(function(reason){ console.log("This run as it is a rejected promise. The reason is ", reason);
});

Вот пример использования метода Promise.resolve, создающего успешно разрешённые промисы.

var promise4 = Promise.resolve(1);
promise4.then(function(value){ console.log("This will run as it is a resovled promise. The resolved value is ", value);
});
promise4.catch(function(reason){ console.log("This will not run as it is a resolved promise", reason);
});

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

var promise4 = Promise.resolve(1);
promise4.then(function(value){ console.log("This will run as it is a resovled promise. The resolved value is ", value);
});
promise4.then(function(value){ console.log("This will also run as multiple handlers can be added. Printing twice the resolved value which is ", value * 2);
});
promise4.catch(function(reason){ console.log("This will not run as it is a resolved promise", reason);
});

Вот что он выведет в консоль браузера:

Использование нескольких .then при работе с промисом

Если, для решения некоей задачи, надо обрабатывать несколько промисов, удобнее всего поместить эти промисы в массив, а затем выполнять с ними необходимые действия. Следующие два метода, Promise.all и Promise.race, предназначены для работы с наборами промисов. Нам удобнее будет воспользоваться чем-то таким, что выдаёт более предсказуемые промисы, что позволит нам понять их поведение. Для того чтобы понять сущность рассматриваемых здесь методов, мы не сможем использовать нашу удобную функцию promiseTRRARNOSG, так как результат её работы слишком сильно зависит от воли случая. Одна из них (promiseTRSANSG) будет создавать промисы, которые разрешаются через n секунд, вторая (promiseTRJANSG ) — промисы, которые через n секунд отклоняются. Поэтому создадим две новых функции.

var promiseTRSANSG = (promiseThatResolvesAfterNSecondsGenerator = function( n = 0
) { return new Promise(function(resolve, reject) { setTimeout(function() { resolve({ resolvedAfterNSeconds: n }); }, n * 1000); });
});
var promiseTRJANSG = (promiseThatRejectsAfterNSecondsGenerator = function( n = 0
) { return new Promise(function(resolve, reject) { setTimeout(function() { reject({ rejectedAfterNSeconds: n }); }, n * 1000); });
});

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

▍Метод Promise.all

Из документации MDN можно узнать, что метод Promise.all(iterable)возвращает промис, который разрешится тогда, когда будут разрешены все промисы, переданные в виде аргумента iterable, или тогда, когда этот аргумент не содержит промисов. Этот промис будет отклонён, если любой из переданных промисов окажется отклонённым.
Рассмотрим несколько примеров.

Пример №1

Здесь будут разрешены все промисы. Такой сценарий встречается чаще всего.

console.time("Promise.All");
var promisesArray = [];
promisesArray.push(promiseTRSANSG(1));
promisesArray.push(promiseTRSANSG(4));
promisesArray.push(promiseTRSANSG(2));
var handleAllPromises = Promise.all(promisesArray);
handleAllPromises.then(function(values) { console.timeEnd("Promise.All"); console.log("All the promises are resolved", values);
});
handleAllPromises.catch(function(reason) { console.log("One of the promises failed with the following reason", reason);
});

Вот что этот код выведет в консоль:

Все промисы разрешены

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

Во-первых, третий промис, на разрешение которого нужно 2 секунды, завершается до завершения второго, но, как можно видеть из вывода, формируемого кодом, порядок промисов в массиве сохраняется.

Во-вторых, в коде присутствует таймер, который используется для того, чтобы выяснить, сколько времени нужно на выполнение инструкции Promise.all.

Однако таймер сообщает нам о том, что вся эта операция заняла, если округлить результат, 4 секунды. Если бы промисы выполнялись последовательно, то время выполнения этой инструкции составило бы 7 секунд (1+4+2). Это является доказательством того, что все промисы выполняются параллельно.

Пример№ 2

Теперь рассмотрим ситуацию, когда в массиве, переданном Promise.all, нет промисов. Полагаю, такой вариант применения этой функции встречается реже всего.

console.time("Promise.All");
var promisesArray = [];
promisesArray.push(1);
promisesArray.push(4);
promisesArray.push(2);
var handleAllPromises = Promise.all(promisesArray);
handleAllPromises.then(function(values) { console.timeEnd("Promise.All"); console.log("All the promises are resolved", values);
});
handleAllPromises.catch(function(reason) { console.log("One of the promises failed with the following reason", reason);
});

Вот какой вывод сформирует этот код:

Вызов Promise.all с передачей этому методу массива, не содержащего промисов

Так как в массиве нет промисов, Promise.all почти мгновенно разрешается.

Пример №3

Теперь посмотрим на то, что происходит в том случае, когда один из промисов, переданных Promise.all, оказывается отклонённым.

console.time("Promise.All");
var promisesArray = [];
promisesArray.push(promiseTRSANSG(1));
promisesArray.push(promiseTRSANSG(5));
promisesArray.push(promiseTRSANSG(3));
promisesArray.push(promiseTRJANSG(2));
promisesArray.push(promiseTRSANSG(4));
var handleAllPromises = Promise.all(promisesArray);
handleAllPromises.then(function(values) { console.timeEnd("Promise.All"); console.log("All the promises are resolved", values);
});
handleAllPromises.catch(function(reason) { console.timeEnd("Promise.All"); console.log("One of the promises failed with the following reason ", reason);
});

Как видно из результатов выполнения кода, показанных ниже, выполнение Promise.all останавливается после первого отклонённого промиса с выводом сообщения, которое выдаёт этот промис.

Выполнение останавливается после первого отклонённого промиса

▍Метод Promise.race

MDN сообщает, что метод Promise.race(iterable)возвращает разрешённый или отклонённый промис со значением или причиной отклонения, после того как один из переданных промисов будет, соответственно, разрешён или отклонён.

Рассмотрим примеры работы с Promise.race.

Пример №1

Здесь показано, что происходит в случае, когда один из промисов, переданных Promise.race, разрешается раньше всех.

console.time("Promise.race");
var promisesArray = [];
promisesArray.push(promiseTRSANSG(4));
promisesArray.push(promiseTRSANSG(3));
promisesArray.push(promiseTRSANSG(2));
promisesArray.push(promiseTRJANSG(3));
promisesArray.push(promiseTRSANSG(4));
var promisesRace = Promise.race(promisesArray);
promisesRace.then(function(values) { console.timeEnd("Promise.race"); console.log("The fasted promise resolved", values);
});
promisesRace.catch(function(reason) { console.timeEnd("Promise.race"); console.log("The fastest promise rejected with the following reason ", reason);
});

Вот что попадает в консоль после выполнения этого примера.

Промис, который разрешился быстрее всех остальных

Третий промис разрешается через 2 секунды. Все промисы здесь выполняются параллельно. Как только это произойдёт, промис, возвращаемый Promise.race, оказывается разрешённым.

Пример №2

Теперь рассмотрим ситуацию, когда один из промисов, переданных Promise.race, оказывается отклонённым.

console.time("Promise.race");
var promisesArray = [];
promisesArray.push(promiseTRSANSG(4));
promisesArray.push(promiseTRSANSG(6));
promisesArray.push(promiseTRSANSG(5));
promisesArray.push(promiseTRJANSG(3));
promisesArray.push(promiseTRSANSG(4));
var promisesRace = Promise.race(promisesArray);
promisesRace.then(function(values) { console.timeEnd("Promise.race"); console.log("The fasted promise resolved", values);
});
promisesRace.catch(function(reason) { console.timeEnd("Promise.race"); console.log("The fastest promise rejected with the following reason ", reason);
});

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

Промис, отклонённый раньше всех

Четвёртый промис отклоняется через 3 секунды. Промисы здесь, как и в предыдущих примерах, выполняются параллельно. Как только это происходит, промис, возвращаемый Promise.race, оказывается отклонённым.

Общий пример и эксперименты

Я собрал все примеры, которые мы рассматривали в этом материале, в одном месте, что позволит удобнее с ними экспериментировать, исследовать различные сценарии работы с промисами. Этот код рассчитан на выполнение в браузере, поэтому тут мы не используем вызовов каких-либо API, не обращаемся к файловым операциям, не работаем с базами данных. Хотя всё это находит применение при разработке реальных проектов, полагаю, работа с этими механизмами может отвлечь нас от нашей главной цели — промисов. А использование простых функций, имитирующих, временные задержки, даёт похожие результаты и не обременяет нас дополнительными деталями.

В частности, вы можете воспользоваться комбинацией методов promiseTRJANSG, promiseTRSANSG и promiseTRRARNOSG для того, чтобы сымитировать множество сценариев использования промисов, что позволит вам лучше их понять. Самостоятельно исследуя эти примеры, вы можете поэкспериментировать с кодом, со значениями переменных, изучить разные сценарии использования промисов. Вот ссылка на gist-страницу с кодом. Кроме того, обратите внимание на то, что использование команды console.time позволяет выяснить время, необходимое на выполнение некоего фрагмента кода, и, например, узнать, параллельно или последовательно выполняются промисы. И, кстати, если хотите — взгляните на библиотеку Bluebird, содержащую некоторые интересные методы для работы с промисами.

Итоги

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

  1. Используйте промисы в ситуациях, когда вы работаете с асинхронным или блокирующим кодом.
  2. Для обработки ситуации успешного разрешения промиса используйте метод .then, для тех случаев, когда промис отклонён, применяйте .catch.
  3. Используйте методы .then и .catch во всех промисах.
  4. Если что-то нужно сделать и при разрешении, и при отклонении промиса, воспользуйтесь методом .finally.
  5. Состояние промиса, после того, как он оказывается разрешённым или отклонённым, уже не меняется.
  6. К одному промису можно добавлять несколько обработчиков, объединяя их вызовы в цепочки.
  7. Все методы объекта Promise, являются ли они статическими методами, или методами прототипа объекта, возвращают промисы.
  8. Метод Promise.all не меняет порядок промисов в переданной ему структуре данных, он не привязывает его к очерёдности разрешения промисов.

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

Уважаемые читатели! Скажите, испытывали ли вы сложности с освоением промисов, когда впервые с ними столкнулись?

КОЕ-ЧТО ВАЖНОЕ И ЦЕННОЕ, ДЛЯ САМЫХ ДОРОГИХ И БЛИЗКИХ

Промо-код на скидку в 10% на наши виртуальные сервера:

Пользуйтесь на здоровье 🙂


Оставить комментарий

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

*

x

Ещё Hi-Tech Интересное!

OpenSceneGraph: Групповые узлы, узлы трансформации и узлы-переключатели

Когда происходит рисование точки, линии или сложного полигона в трехмерном мире, финальный результат, в конечном итоге, будет изображен на плоском, двухмерном экране. Соответственно, трехмерные объекты проходят некий путь преобразования, превращаясь в набор пикселей, выводимых в двумерное окно. Идеологически и «чистые» ...

«Монстры в играх или 15 см достаточно для атаки»

Спасибо читателям за проявленный интерес, рад что материалы полезны. Представляю вам четвертую статью цикла.Сегодня я хотел бы разобрать такой важный элемент для любого монстра — дистанция атаки. Их существует всего три: ближняя средняя дальняя В первой части разберем типичные примеры ...