Использование промисов
Promise (промис) — это объект, представляющий результат успешного или неудачного завершения асинхронной операции. Так как большинство людей пользуются уже созданными промисами, это руководство начнём с объяснения использования вернувшихся промисов до объяснения принципов создания.
В сущности, промис — это возвращаемый объект, в который вы записываете два колбэка вместо того, чтобы передать их функции.
Например, вместо старомодной функции, которая принимает два колбэка и вызывает один из них в зависимости от успешного или неудачного завершения операции:
function doSomethingOldStyle(successCallback, failureCallback) console.log("Готово."); // Успех в половине случаев. if (Math.random() > 0.5) successCallback("Успех"); > else failureCallback("Ошибка"); > > function successCallback(result) console.log("Успешно завершено с результатом " + result); > function failureCallback(error) console.log("Завершено с ошибкой " + error); > doSomethingOldStyle(successCallback, failureCallback);
…современные функции возвращают промис, в который вы записываете ваши колбэки:
function doSomething() return new Promise((resolve, reject) => console.log("Готово."); // Успех в половине случаев. if (Math.random() > 0.5) resolve("Успех"); > else reject("Ошибка"); > >); > const promise = doSomething(); promise.then(successCallback, failureCallback);
doSomething().then(successCallback, failureCallback);
Мы называем это асинхронным вызовом функции. У этого соглашения есть несколько преимуществ. Давайте рассмотрим их.
Гарантии
В отличие от старомодных переданных колбэков промис даёт некоторые гарантии:
- Колбэки никогда не будут вызваны до завершения обработки текущего события в событийном цикле JavaScript.
- Колбэки, добавленные через .then даже после успешного или неудачного завершения асинхронной операции, будут также вызваны.
- Несколько колбэков может быть добавлено вызовом .then нужное количество раз, и они будут выполняться независимо в порядке добавления.
Но наиболее непосредственная польза от промисов — цепочка вызовов (chaining).
Цепочка вызовов
Общая нужда — выполнять две или более асинхронных операции одна за другой, причём каждая следующая начинается при успешном завершении предыдущей и использует результат её выполнения. Мы реализуем это, создавая цепочку вызовов промисов (promise chain).
Вот в чём магия: функция then возвращает новый промис, отличающийся от первоначального:
let promise = doSomething(); let promise2 = promise.then(successCallback, failureCallback);
let promise2 = doSomething().then(successCallback, failureCallback);
Второй промис представляет завершение не только doSomething() , но и функций successCallback или failureCallback , переданных вами, а они тоже могут быть асинхронными функциями, возвращающими промис. В этом случае все колбэки, добавленные к promise2 будут поставлены в очередь за промисом, возвращаемым successCallback или failureCallback .
По сути, каждый вызванный промис означает успешное завершение предыдущих шагов в цепочке.
Раньше выполнение нескольких асинхронных операций друг за другом приводило к классической «Вавилонской башне» колбэков:
doSomething(function (result) doSomethingElse( result, function (newResult) doThirdThing( newResult, function (finalResult) console.log("Итоговый результат: " + finalResult); >, failureCallback, ); >, failureCallback, ); >, failureCallback);
В современных функциях мы записываем колбэки в возвращаемые промисы — формируем цепочку промисов:
doSomething() .then(function (result) return doSomethingElse(result); >) .then(function (newResult) return doThirdThing(newResult); >) .then(function (finalResult) console.log("Итоговый результат: " + finalResult); >) .catch(failureCallback);
Аргументы then необязательны, а catch(failureCallback) — это сокращение для then(null, failureCallback) . Вот как это выражено с помощью стрелочных функций:
doSomething() .then((result) => doSomethingElse(result)) .then((newResult) => doThirdThing(newResult)) .then((finalResult) => console.log(`Итоговый результат: $finalResult>`); >) .catch(failureCallback);
Важно: Всегда возвращайте промисы в return, иначе колбэки не будут сцеплены и ошибки могут быть не пойманы (стрелочные функции неявно возвращают результат, если скобки <> вокруг тела функции опущены).
Цепочка вызовов после catch
Можно продолжить цепочку вызовов после ошибки, т. е. после catch , что полезно для выполнения новых действий даже после того, как действие вернёт ошибку в цепочке вызовов. Ниже приведён пример:
new Promise((resolve, reject) => < console.log('Начало'); resolve(); >) .then(() => < throw new Error('Где-то произошла ошибка'); console.log('Выведи это'); >) .catch(() => < console.log('Выведи то'); >) .then(() => < console.log('Выведи это, несмотря ни на что'); >);
В результате выведется данный текст:
Начало Выведи то Выведи это, несмотря ни на что
Заметьте, что текст «Выведи это» не вывелся, потому что «Где-то произошла ошибка» привела к отказу
Распространение ошибки
Вы могли ранее заметить, что failureCallback повторяется три раза в «pyramid of doom», а в цепочке промисов всего лишь один раз:
doSomething() .then(result => doSomethingElse(result)) .then(newResult => doThirdThing(newResult)) .then(finalResult => console.log(`Итоговый результат: $`)) .catch(failureCallback);
В основном, цепочка промисов останавливает выполнение кода, если где-либо произошла ошибка, и вместо этого ищет далее по цепочке обработчики ошибок. Это очень похоже на то, как работает синхронный код:
try < let result = syncDoSomething(); let newResult = syncDoSomethingElse(result); let finalResult = syncDoThirdThing(newResult); console.log(`Итоговый результат: $`); > catch(error)
Эта симметрия с синхронным кодом лучше всего показывает себя в синтаксическом сахаре async / await в ECMAScript 2017:
async function foo() < try < let result = await doSomething(); let newResult = await doSomethingElse(result); let finalResult = await doThirdThing(newResult); console.log(`Итоговый результат: $`); > catch(error) < failureCallback(error); >>
Работа данного кода основана на промисах. Для примера здесь используется функция doSomething() , которая встречалась ранее. Вы можете прочитать больше о синтаксисе здесь
Промисы решают основную проблему пирамид, обработку всех ошибок, даже вызовов исключений и программных ошибок. Это основа для функционального построения асинхронных операций.
Создание промиса вокруг старого колбэка
Promise может быть создан с помощью конструктора. Это может понадобится только для старых API.
В идеале, все асинхронные функции уже должны возвращать промис. Но увы, некоторые APIs до сих пор ожидают успешного или неудачного колбэка переданных по старинке. Типичный пример: setTimeout() функция:
setTimeout(() => saySomething("10 seconds passed"), 10000);
Смешивание старого колбэк-стиля и промисов проблематично. В случае неудачного завершения saySomething или программной ошибки, нельзя обработать ошибку.
К счастью мы можем обернуть функцию в промис. Хороший тон оборачивать проблематичные функции на самом низком возможном уровне, и больше никогда их не вызывать напрямую:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); wait(10000).then(() => saySomething("10 seconds")).catch(failureCallback);
В сущности, конструктор промиса становится исполнителем функции, который позволяет нам резолвить или режектить промис вручную. Так как setTimeout всегда успешен, мы опустили reject в этом случае.
Композиция
Promise.resolve() и Promise.reject() короткий способ создать уже успешные или отклонённые промисы соответственно. Это иногда бывает полезно.
Promise.all() и Promise.race() — два метода запустить асинхронные операции параллельно.
Последовательное выполнение композиции возможно при помощи хитрости JavaScript:
[func1, func2].reduce((p, f) => p.then(f), Promise.resolve());
Фактически, мы превращаем массив асинхронных функций в цепочку промисов равносильно: Promise.resolve().then(func1).then(func2);
Это также можно сделать, объединив композицию в функцию, в функциональном стиле программирования:
const applyAsync = (acc,val) => acc.then(val); const composeAsync = (. funcs) => x => funcs.reduce(applyAsync, Promise.resolve(x));
composeAsync функция примет любое количество функций в качестве аргументов и вернёт новую функцию которая примет в параметрах начальное значение, переданное по цепочке. Это удобно, потому что некоторые или все функции могут быть либо асинхронными, либо синхронными, и они гарантированно выполнятся в правильной последовательности:
const transformData = composeAsync(func1, asyncFunc1, asyncFunc2, func2); transformData(data);
В ECMAScript 2017, последовательные композиции могут быть выполнены более простым способом с помощью async/await:
for (const f of [func1, func2])
Порядок выполнения
Чтобы избежать сюрпризов, функции, переданные в then никогда не будут вызваны синхронно, даже с уже разрешённым промисом:
Promise.resolve().then(() => console.log(2)); console.log(1); // 1, 2
Вместо немедленного выполнения, переданная функция встанет в очередь микрозадач, а значит выполнится, когда очередь будет пустой в конце текущего вызова JavaScript цикла событий (event loop), т.е. очень скоро:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); wait().then(() => console.log(4)); Promise.resolve().then(() => console.log(2)).then(() => console.log(3)); console.log(1); // 1, 2, 3, 4
Вложенность
Простые цепочки promise лучше оставлять без вложений, так как вложенность может быть результатом небрежной структуры. Смотрите распространённые ошибки.
Вложенность — это управляющая структура, ограничивающая область действия операторов catch. В частности, вложенный catch только перехватывает сбои в своей области и ниже, а не ошибки выше в цепочке за пределами вложенной области. При правильном использовании это даёт большую точность в извлечение ошибок:
doSomethingCritical() .then(result => doSomethingOptional() .then(optionalResult => doSomethingExtraNice(optionalResult)) .catch(e => <>)) // Игнорируется если необязательные параметр не выкинул исключение .then(() => moreCriticalStuff()) .catch(e => console.log("Критическая ошибка: " + e.message));
Обратите внимание, что необязательный шаги здесь выделены отступом.
Внутренний оператор catch нейтрализует и перехватывает ошибки только от doSomethingOptional() и doSomethingExtraNice(), после чего код возобновляется с помощью moreCriticalStuff(). Важно, что в случае сбоя doSomethingCritical() его ошибка перехватывается только последним (внешним) catch.
Частые ошибки
В этом разделе собраны частые ошибки, возникающие при создании цепочек промисов. Несколько таких ошибок можно увидеть в следующем примере:
// Плохой пример! Три ошибки! doSomething().then(function(result) < doSomethingElse(result) // Забыл вернуть промис из внутренней цепочки + неуместное влаживание .then(newResult =>doThirdThing(newResult)); >).then(() => doFourthThing()); // Забыл закончить цепочку методом catch
Первая ошибка это неправильно сцепить вещи между собой. Такое происходит когда мы создаём промис но забываем вернуть его. Как следствие, цепочка сломана, но правильнее было бы сказать что теперь у нас есть две независимые цепочки, соревнующиеся за право разрешится первой. Это означает, что doFourthThing() не будет ждать doSomethingElse() или doThirdThing() пока тот закончится, и будет исполнятся параллельно с ними, это, вероятно, не то что хотел разработчик. Отдельные цепочки также имеют отдельную обработку ошибок, что приводит к необработанным ошибкам.
Вторая ошибка это излишняя вложенность, включая первую ошибку. Вложенность также ограничивает область видимости внутренних обработчиков ошибок, если это не то чего хотел разработчик, это может привести к необработанным ошибкам. Примером этого является пример как не нужно создавать промисы, который комбинирует вложенность с чрезмерным использованием конструктора промисов для оборачивания кода который уже использует промисы.
Третья ошибка это забыть закончить цепочку ключевым словом catch . Незаконченные цепочки приводят к необработанным отторжениям промисов в большинстве браузеров.
Хорошим примером является всегда либо возвращать либо заканчивать цепочки промисов, и как только вы получаете новый промис, возвращайте его сразу же, чтобы не усложнять код излишней вложенностью:
doSomething() .then(function(result) < return doSomethingElse(result); >) .then(newResult => doThirdThing(newResult)) .then(() => doFourthThing()) .catch(error => console.log(error));
Обратите внимание что () => x это сокращённая форма () => < return x; >.
Теперь у нас имеется единственная определённая цепочка с правильной обработкой ошибок.
Использование async / await предотвращает большинство, если не все вышеуказанные ошибки, но взамен появляется другая частая ошибка — забыть ключевое слово await .
Смотрите также
- Promise.then()
- Спецификация Promises/A+ (EN)
- Нолан Лоусон (Nolan Lawson): У нас проблемы с промисами — распространённые ошибки (EN)
Found a content problem with this page?
- Edit the page on GitHub.
- Report the content issue.
- View the source on GitHub.
.then ( )
Обрабатываем ситуацию, когда пообещали значение — и выполнили обещание.
Время чтения: меньше 5 мин
Открыть/закрыть навигацию по статье
Обновлено 29 мая 2022
Эта статья связана с понятием Promise.
Кратко
Скопировать ссылку «Кратко» Скопировано
Метод then ( ) используют, чтобы выполнить код после изменения состояния промиса.
Метод принимает два аргумента:
- on Fulfill — функция-колбэк, которая будет вызвана при переходе промиса в состояние «успех» fulfilled . Функция имеет один параметр, в который передаётся результат выполнения операции
- on Reject — функция-колбэк, которая будет вызвана при переходе промиса в состояние «ошибка» rejected . Функция имеет один параметр, в который передаётся информация об ошибке
Всегда возвращает новый промис.
Как пишется
Скопировать ссылку «Как пишется» Скопировано
// getPasswords() — асинхронная функция, которая возвращает промисgetPasswords().then( function (result) // что-то делаем с результатом операции console.log('Все пароли:' + result) >, function (err) // обрабатываем ошибку console.error(err.message) >)// getPasswords() — асинхронная функция, которая возвращает промис getPasswords().then( function (result) // что-то делаем с результатом операции console.log('Все пароли:' + result) >, function (err) // обрабатываем ошибку console.error(err.message) > )
Как понять
Скопировать ссылку «Как понять» Скопировано
Обработка асинхронных операций через промис и then ( ) очень похожа на работу с колбэками.
Так как then ( ) всегда возвращает новый промис, то его удобно использовать для построения последовательностей асинхронных операций:
// запросим через API список домов из Игры престолов. Метод `fetch` возвращает промисfetch('https://www.anapioficeandfire.com/api/houses') .then(function (response) // выполнится, когда от API придет ответ // запустим асинхронную операцию парсинга JSON из ответа сервера return response.json() // вернем из обработчика промис, к которому добавим then >) .then(function (houses) // выполнится, когда JSON распарсится return fetch(houses[0].overlord) // запросим данные о сюзерене этого дома >) .then(function (response) // выполнится, когда от API придет ответ return response.json() >) .then(function (overlord) console.log(overlord.name) >)// запросим через API список домов из Игры престолов. Метод `fetch` возвращает промис fetch('https://www.anapioficeandfire.com/api/houses') .then(function (response) // выполнится, когда от API придет ответ // запустим асинхронную операцию парсинга JSON из ответа сервера return response.json() // вернем из обработчика промис, к которому добавим then >) .then(function (houses) // выполнится, когда JSON распарсится return fetch(houses[0].overlord) // запросим данные о сюзерене этого дома >) .then(function (response) // выполнится, когда от API придет ответ return response.json() >) .then(function (overlord) console.log(overlord.name) >)
В коде выше, каждый вызов then ( ) привязан к результату предыдущей операции. Такой код читается почти как синхронный.
На практике
Скопировать ссылку «На практике» Скопировано
Николай Лопин советует
Скопировать ссылку «Николай Лопин советует» Скопировано
then ( ) в индустрии используется только для обработки успешного завершения операции, в варианте с одним аргументом:
getPasswords().then(function (result) // что-то делаем с результатом операции console.log(`Все пароли: $`)>)getPasswords().then(function (result) // что-то делаем с результатом операции console.log(`Все пароли: $result>`) >)
Для обработки ошибок используют метод catch ( ) . Такие цепочки читаются лучше:
getPasswords() .then(function (result) console.log(`Все пароли: $`) >) .catch(function (error) console.log(`Ошибка: $`) >)getPasswords() .then(function (result) console.log(`Все пароли: $result>`) >) .catch(function (error) console.log(`Ошибка: $error.message>`) >)
Then js что это
Ранее мы рассмотрели, как из функции промиса мы можем передать во вне результат асинхронной операции:
const myPromise = new Promise(function(resolve)< console.log("Выполнение асинхронной операции"); resolve("Привет мир!"); >);
Теперь получим это значение. Для получения результата операции промиса применяется функция then() объекта Promise :
then(onFulfilled, onRejected);
Первый параметр функции — onFulfilled представляет функцию, которая выполняется при успешном завершении промиса и в качестве параметра получает переданные в resolve() данные.
Второй параметр функции — onRejected представляет функцию, которая выполняется при возникновении ошибки и в качестве параметра получает переданные в reject() данные.
Функция then() возвращает также объект Promise .
Так, получим переданные данные:
const myPromise = new Promise(function(resolve)< console.log("Выполнение асинхронной операции"); resolve("Привет мир!"); >); myPromise.then(function(value)< console.log(`Из промиса получены данные: $`); >)
То есть параметр value здесь будет представлять строку «Привет мир!» , которая передается в resolve(«Привет мир!») . В итоге консольный вывод будет выглядеть следующим образом:
Выполнение асинхронной операции Из промиса получены данные: Привет мир!
Для примера вызове несколько промисов, чтобы увидеть асинхронность в деле:
const myPromise3000 = new Promise(function(resolve)< console.log("[myPromise3000] Выполнение асинхронной операции"); setTimeout(()=>, 3000); >); const myPromise1000 = new Promise(function(resolve)< console.log("[myPromise1000] Выполнение асинхронной операции"); setTimeout(()=>, 1000); >); const myPromise2000 = new Promise(function(resolve)< console.log("[myPromise2000] Выполнение асинхронной операции"); setTimeout(()=>, 2000); >); myPromise3000.then((value)=>console.log(value)); myPromise1000.then((value)=>console.log(value)); myPromise2000.then((value)=>console.log(value));
Здесь определены три однотипных промиса. Чтобы каждый из них не выполнялся сразу, они используют функцию setTimeout и устанавливают возвращаемое значение только через несколько секунд. Для разных промисов длительность задержки различается. И в данном случае мы получим следующий консольный вывод:
[myPromise3000] Выполнение асинхронной операции [myPromise1000] Выполнение асинхронной операции [myPromise2000] Выполнение асинхронной операции [myPromise1000] Привет мир! [myPromise2000] Привет мир! [myPromise3000] Привет мир!
Здесь мы видим, что первым начал выполняться промис myPromise3000, однако он же завершился последним, так как для него установлено наибольшее время задержки — 3 секунды. Однако его задержка не помешала выполнению остальных промисов.
При этом нам необязательно вообще передавать в resolve() какое-либо значение. Возможно, асинхронная операция просто выполняется и передает во вне никакого результата.
const x = 4; const y = 8; const myPromise = new Promise(function()< console.log("Выполнение асинхронной операции"); const z = x + y; console.log(`Результат операции: $`) >); myPromise.then();
В данном случае функция в промисе вычисляет сумму чисел x и y и выводит результат на консоль.
Метод Promise.resolve
Иногда требуется просто вернуть из промиса некоторое значение. Для этого можно использовать метод Promise.resolve() . В этот метод передается возвращаемое из промиса значение. Метод Promise.resolve() возвращает объект Promise:
const myPromise = Promise.resolve("Привет мир!"); myPromise.then(value => console.log(value)); // Привет мир!
Определение промиса через функцию
Нередко промис определяется через функцию, которая возвращет объект Promise. Например:
function sum(x, y)< return new Promise(function(resolve)< const result = x + y; resolve(result); >) > sum(3, 5).then(function(value)< console.log("Результат операции:", value);>); sum(25, 4).then(function(value)< console.log("Сумма чисел:", value);>);
Здесь функция sum() принимает два числа и возвращает промис, который инкапсулирует операцию сумму этих чисел. После вычисления сумма чисел передается в resolve() , соответственно мы ее затем можем получить через метод then() . Определение промиса через функцию позволяет нам, с одной стороны, при вызове функции передавать разные значения. А с другой стороны, работать с результатом этой функции как с промисом и настроить при каждом конкретном вызове обработку полученного значения.
Результат работы программы:
Результат операции: 8 Сумма чисел: 29
Однако, что если у нас совпадает принцип обработки полученного из асинхронной функции значения?
sum(3, 5).then(function(value)< console.log("Результат операции:", value);>); sum(25, 4).then(function(value)< console.log("Результат операции:", value);>);
В этом случае логика обработки будет повторяться. Но поскольку метод then() также возвращает объект Promise, то мы можем сделать следующим образом:
function sum(x, y)< return new Promise(function(resolve)< const result = x + y; resolve(result); >).then(function(value)< console.log("Результат операции:", value);>); > sum(3, 5); sum(25, 4);
Гибкая настройка функции
А что, если мы хотим, чтобы у программиста был выбор: если он хочет, то может определить свой обработчик, а если нет, то применяется некоторый обработчик по умолчанию. В этом случае мы можем определить функцию обработчика в качестве параметра функции, а если он не передан, то устанавливать обработчик по умолчанию:
function sum(x, y, func)< // если обработчик не установлен, то устанавливаем обработчик по умолчанию if(func===undefined) func = function(value)< console.log("Результат операции:", value);>; return new Promise(function(resolve)< const result = x + y; resolve(result); >).then(func); > sum(3, 5); sum(25, 4, function(value)< console.log("Сумма:", value);>);
Здесь при первом вызове функции sum() ( sum(3, 5) ) будет срабатывать обработчик по умолчанию. Во втором случае обработчик явным образом передается через третий параметр, соответственно он будет задействован sum(25, 4, function(value)< console.log("Сумма:", value);>)
JavaScript метод Promise.then()

JavaScript метод .then() объекта Promise добавляет обработчик, или обработчики, вызываемые когда объект Promise имеет состояние fulfilled (успешное выполнение), или rejected (выполнение отклонено).
Поддержка браузерами
| Метод | Chrome | Firefox | Opera | Safari | IExplorer | Edge |
|---|---|---|---|---|---|---|
.then() ![]() |
32.0 | 29.0 | 19.0 | 8.0 | Нет | Да |
JavaScript синтаксис:
p.then(onFulfilled); p.then(onFulfilled, onRejected); onFulfilled - Function onRejected - Function
После выполнения или отклонения обещания соответствующая функция обработчика (onFulfilled, или onRejected) будет вызвана асинхронно (запланирована в текущем цикле потока). Поведение функции обработчика соответствует определенному набору правил. Если функция обработчика:
- возвращает значение, то обещание, возвращенное методом .then()
разрешается с этим значением; - ничего не возвращает, то обещание, возвращенное методом .then()
разрешается с неопределенным значением ( undefined ); - вызывает ошибку, то обещание, возвращенное методом .then()
отклоняется с вызванной ошибкой в качестве его значения; - возвращает уже разрешенное обещание, то обещание, возвращенное методом .then()
разрешается со значением этого обещания в качестве его значения; - возвращает уже отклоненное обещание, то обещание, возвращенное методом .then()
отклоняется со значением этого обещания в качестве его значения; - возвращает еще один объект Promise находящийся в состоянии ожидания, то разрешение, или отклонение обещания, возвращенного методом .then()
будет следовать за разрешением, или отклонением обещания, возвращенного обработчиком. Кроме того, значение обещания, возвращаемого методом .then()
будет таким же, как значение обещания, возвращаемого обработчиком.
Спецификация
ECMAScript 2015 (6th Edition, ECMA-262)
Значения параметров
| Параметр | Описание |
|---|---|
| onFulfilled | Функция, которая будет вызвана, когда обещание выполнено. Эта функция имеет только один аргумент — значение с которым обещание было выполнено. Если это не функция, она внутренне заменяется функцией «Identity» (возвращает полученный аргумент). |
| onRejected | Функция, которая будет вызвана, когда обещание отклонено. Эта функция имеет только один аргумент — значение с которым обещание было отклонено. Если это не функция, она внутренне заменяется функцией «Thrower» (она выдает ошибку, полученную в качестве аргумента). Необязательный аргумент. |
Пример использования
Базовое использование обрабочиков
const promise = Promise.resolve("Успешно"); // обещание успешно выполнено с заданным значением const promise2 = Promise.reject(new Error("Отклонено")); // обещание отклонено с заданным значением promise.then(function(value) // Если успешно выполнен (fulfilled) console.log(value); >, function (reason) // Если завершился ошибкой (rejected) console.log(reason); >); // Успешно // использование стрелочной функции promise2.then( val => console.log(elem), // обработчик для успешного выполнения reason => console.log(reason.message)); // обработчик при отклоненном выполнении // Отклонено
В этом примере мы инициализировали две переменные, которые содержат объект Promise , в первом случае с использованием метода resolve() обещание было успешно выполнено с заданным значением, а во втором с использованием метода reject() отклонено.

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

В следующем примере мы рассмотрим с Вами как составлять цепочки вызовов с использованием метода then():
const promise = Promise.resolve(1); // обещание успешно выполнено с заданным значением promise.then(val => val = val + 1; console.log(val); return val>) // обработчик для успешного выполнения .then(val => val = val + 1; console.log(val); return val>) // обработчик для успешного выполнения .then(val => val = val + 1; console.log(val); return val>) // обработчик для успешного выполнения // 2 // 3 // 4

В этом примере мы инициализировали переменную, которая содержит объект Promise , который мы вернули с использованием метода resolve() (обещание было успешно выполнено с заданным значением).
С использованием метода then() мы добавили обработчики, вызываемые когда объект Promise имеет состояние fulfilled (успешное выполнение). Обратите внимание, что при каждом последующем вызове метода then() передается новое значение (увеличиваем переданное значение из предыдущего вызова на единицу), в результате чего в консоль будут выведены три значения — 2, 3 и 4.
Обработка ошибок в обработчиках
В следудующем примере мы с Вами рассмотрим ситуацию при которой в вызове обработчика, предназначенного для обработки состояния fulfilled (успешное выполнение) объекта Promise происходит ошибка:
const promise = Promise.resolve(); // обещание успешно выполнено с заданным значением promise.then(() => throw new Error("Ошибочка вышла")>) // генерируем исключение и возвращаем отклоненное обещание .then( val => console.log( val ), // обработчик не будет вызван err => console.log( err.message )); // обработчик при отклоненном выполнении // Ошибочка вышла
В этом примере мы инициализировали переменную, которая содержит объект Promise , который мы вернули с использованием метода resolve() (обещание было успешно выполнено с заданным значением). С использованием методов then() мы добавили цепочку вызовов.
Обратите внимание, что внутри первого вызова метода then() мы генерируем исключение с определенном текстом ошибки. Что приводит к тому, что мы возвращаем объект Promise выполнение которого было отклонено. В результате следующий вызов метода then() будет обрабатывать ошибку, то есть сработает функция, которая предназначена для случая, когда обещание отклонено (второй аргумент).
На практике, как правило, для удобства и лучшей читаемости кода при обработке ошибок используют метод catch() , а не используют синтаксис метода then() с двумя аргументами. Рассмотрим следующий пример:
const promise = Promise.resolve(); // обещание успешно выполнено с заданным значением promise.then(() => throw new Error("Ошибочка вышла")>) // генерируем исключение и возвращаем отклоненное обещание .catch(error => console.log(error.message)) // добавляем обработчик для отклоненного обещания .then(() => console.log("Обработчик будет вызван" )); // обработчик для успешного выполнения // Ошибочка вышла // Обработчик будет вызван

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

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

В следующем примере мы рассмотрим ситуацию в которой метод catch() будет генерировать исключение:
const promise = Promise.reject(new Error("Ошибочка")); // обещание отклонено promise.catch(error => < // добавляем обработчик для отклоненного обещания console.log(error.message); // выводит в консоль текст ошибки throw new Error("Ошибочка 2"); // генерируем исключение и возвращаем отклоненное обещание >).catch(error => < // добавляем обработчик для отклоненного обещания console.log(error.message); // выводит в консоль текст ошибки throw new Error("Ошибочка 3");// генерируем исключение и возвращаем отклоненное обещание >).catch(error => console.log(error.message)) // добавляем обработчик для отклоненного обещания и выводим в консоль текст ошибки .then(() => console.log("Наконец без ошибок" )); // обработчик для успешного выполнения // Ошибочка // Ошибочка 2 // Ошибочка 3 // Наконец без ошибок

В этом примере мы инициализировали переменную, которая содержит объект Promise , который мы вернули с использованием метода reject() (обещание было отклонено).
Для обработки ошибок мы используем цепочку вызовов, которую мы составили с использованием метода catch() . Обратите внимание, что первые два вызова метода catch() помимо того, что выводят в консоль текст ошибки, дополнительно еще генерируют исключение.
Третий метод catch() просто выводит в консоль текст ошибки и возвращает обещание, которое имеет состояние fulfilled (успешное выполнение), которое мы и обрабатываем в методе then() , который находится в конце цепочки.
Нюансы асинхронного выполнения

В следующих примерах мы рассмотрим нюансы асинхронного выполнения при использовании цепных методов then():
const promise = Promise.resolve("hello"); // обещание успешно выполнено с заданным значением promise.then(function(val) // обработчик для успешного выполнения return new Promise(function(resolve, reject) // возвращаем новое обещание setTimeout(function() // вызываем функцию через указанное число миллисекунд val + nam"> world"; // изменяем полученное значение console.log(1, val); // выводим значение в консоль resolve(val); // изменяем состояние обещания на fulfilled (успешное выполнение) >, 1000); >); >) .then(function(val) // обработчик для успешного выполнения setTimeout(function() // вызываем функцию через указанное число миллисекунд val += " . "; // изменяем полученное значение console.log(2, val); // выводим значение в консоль >, 1000) return val; // возвращаем значение >) .then(function(val) // обработчик для успешного выполнения console.log(3, val); // выводим значение в консоль >); // 1 "hello world" // 3 "hello world" // 2 "hello world . "
В этом примере мы инициализировали переменную, которая содержит объект Promise , который мы вернули с использованием метода resolve() (обещание было успешно выполнено). С использованием метода then() мы добавили обработчики, вызываемые когда объект Promise имеет состояние fulfilled (успешное выполнение).
Внутри первого обработчика then() мы возвращаем новое обещание, которое с использованием функции setTimeout() через одну секунду вызовет метод resolve() , который вернет обещание, которое было успешно выполнено.

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

Последний обработчик then() просто выводит в консоль полученное значение. В следующем примере мы рассмотрим как вывести значение по порядку:
const promise = Promise.resolve("hello"); // обещание успешно выполнено с заданным значением promise.then(function(val) // обработчик для успешного выполнения return new Promise(function(resolve, reject) // возвращаем новое обещание setTimeout(function() // вызываем функцию через указанное число миллисекунд val + nam"> world"; // изменяем полученное значение console.log(1, val); // выводим значение в консоль resolve(val); // изменяем состояние обещания на fulfilled (успешное выполнение) >, 1000); >); >) .then(function(val) // обработчик для успешного выполнения return new Promise(function(resolve, reject) // возвращаем новое обещание setTimeout(function() // вызываем функцию через указанное число миллисекунд val + nam"> . "; // изменяем полученное значение console.log(2, val); // выводим значение в консоль resolve(val); // изменяем состояние обещания на fulfilled (успешное выполнение) >, 1000); >); >) .then(function(val) // обработчик для успешного выполнения console.log(3, val); // выводим значение в консоль >); // 1 "hello world" // 2 "hello world . " // 3 "hello world . "
По аналогии с предыдущим примером мы инициализировали переменную, которая содержит объект Promise , который мы вернули с использованием метода resolve() (обещание было успешно выполнено), и с использованием метода then() добавили обработчики.

Как вы могли догадаться, в отличии от предыдущего примера нам достаточно во втором методе then() вернуть обещание по аналогии с первым вызовом этого метода. В этом случае следующий обработчик не будет вызван до тех пор пока предыдущее обещание не будет выполнено, или отклонено. Зная эти нюансы вы сможете эффективно выполнять свою работу исходя из поставленной задачи.
© 2016-2024 Денис Большаков, замечания и предложения по работе сайта Вы можете направить по адресу basicweb.ru@gmail.com
Кажется, вы используете блокировщик рекламы 🙁
Этот ресурс не является коммерческим проектом и развивается на энтузиазме автора сайта, который посвящает все свое свободное время этому проекту.
Добавьте, пожалуйста, basicweb.ru в исключения блокировщика рекламы, этим вы поможете развитию проекта.