JavaScript метод Promise.prototype.then()
Определение и применение
JavaScript метод .then() объекта
Promise
добавляет обработчик, или обработчики, вызываемые когда объект Promise имеет состояние
fulfilled (успешное выполнение), или rejected (выполнение отклонено).
Метод возвращает новое обещание. Обработчики вызываются асинхронно через очередь микрозадач, даже если результат уже известен.
Возвращённое обработчиком значение передаётся дальше; возвращённый thenable принимается, а исключение отклоняет новое
обещание. Пропущенные обработчики сохраняют значение или причину отклонения. Второй аргумент того же
then() не ловит исключение из первого: для этого нужен следующий catch().
Поддержка браузерами
Спецификация: ecma262
Поддерживается во всех основных браузерах с 2015 года.
| Возможность | Компьютеры | Мобильные | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
then | 32 | 12 | 29 | 19 | 8 | 32 | 29 | 19 | 8 | 2.0 | 4.4.3 | 8 |
поддерживаетсяс ограниченияминет поддержкинет данных≤31 — не позднее версии 31
JavaScript синтаксис
p.then(onFulfilled);
p.then(onFulfilled, onRejected);
После выполнения или отклонения обещания соответствующая функция обработчика (onFulfilled, или
onRejected) будет вызвана асинхронно (через очередь микрозадач). Поведение функции
обработчика соответствует определенному набору правил. Если функция обработчика:
-
возвращает значение, то обещание, возвращенное методом
.then()разрешается с этим значением; -
ничего не возвращает, то обещание, возвращенное методом
.then()разрешается с неопределенным значением (undefined); -
вызывает ошибку, то обещание, возвращенное методом
.then()отклоняется с вызванной ошибкой в качестве его значения; -
возвращает уже разрешенное обещание, то обещание, возвращенное методом
.then()разрешается со значением этого обещания в качестве его значения; -
возвращает уже отклоненное обещание, то обещание, возвращенное методом
.then()отклоняется со значением этого обещания в качестве его значения; -
возвращает еще один объект
Promiseнаходящийся в состоянии ожидания, то разрешение, или отклонение обещания, возвращенного методом.then()будет следовать за разрешением, или отклонением обещания, возвращенного обработчиком. Кроме того, значение обещания, возвращаемого методом.then()будет таким же, как значение обещания, возвращаемого обработчиком.
Спецификация
ECMAScript: Promise.prototype.then()
Значения параметров
| Параметр | Описание |
|---|---|
onFulfilled |
Функция, которая будет вызвана, когда обещание выполнено. Эта функция имеет только один аргумент - значение с которым обещание было выполнено. Если это не функция, она внутренне заменяется функцией "Identity" (возвращает полученный аргумент). |
onRejected |
Функция, которая будет вызвана, когда обещание отклонено. Эта функция имеет только один аргумент - значение с которым обещание было отклонено. Если это не функция, она внутренне заменяется функцией "Thrower" (она выдает ошибку, полученную в качестве аргумента). Необязательный аргумент. |
Примеры, попробуйте сами
Базовое использование обработчиков
В примере «Обработка успеха и ошибки» показан результат вызова метода.
Обработка успеха и ошибки
Измените значение или результат обработчика и сравните порядок сообщений после запуска.
index.html
<html lang="ru">
<head>
<meta charset="UTF-8" />
<title>Обработка успеха и ошибки</title>
</head>
<body>
<p>Результат выполнения выводится в консоль.</p>
</body>
</html>
main.js
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(val), // обработчик для успешного выполнения
(reason) => console.log(reason.message)
) // обработчик при отклоненном выполнении
// Отклонено
В этом примере мы инициализировали две переменные, которые содержат объект
Promise, в первом случае с использованием метода
resolve()
обещание было успешно выполнено с заданным значением, а во втором с использованием метода
reject()
отклонено.
С использованием метода then() мы добавили обработчики, вызываемые когда объект
Promise
имеет состояние fulfilled (успешное выполнение), или rejected (выполнение отклонено). В
первом случае будет срабатывать обработчик для успешного выполнения, а во втором обработчик при
отклоненном выполнении.
Составление цепных вызовов
В следующем примере мы рассмотрим с Вами как составлять цепочки вызовов с использованием метода then():
В примере «Передача значения по цепочке» показан результат вызова метода.
Передача значения по цепочке
Измените значение или результат обработчика и сравните порядок сообщений после запуска.
index.html
<html lang="ru">
<head>
<meta charset="UTF-8" />
<title>Передача значения по цепочке</title>
</head>
<body>
<p>Результат выполнения выводится в консоль.</p>
</body>
</html>
main.js
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 происходит ошибка:
В примере «Исключение в обработчике» показан результат вызова метода.
Исключение в обработчике
Измените значение или результат обработчика и сравните порядок сообщений после запуска.
index.html
<html lang="ru">
<head>
<meta charset="UTF-8" />
<title>Исключение в обработчике</title>
</head>
<body>
<p>Результат выполнения выводится в консоль.</p>
</body>
</html>
main.js
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() с двумя аргументами. Рассмотрим следующий пример:
В примере «Продолжение после catch» показан результат вызова метода.
Продолжение после catch
Измените значение или результат обработчика и сравните порядок сообщений после запуска.
index.html
<html lang="ru">
<head>
<meta charset="UTF-8" />
<title>Продолжение после catch</title>
</head>
<body>
<p>Результат выполнения выводится в консоль.</p>
</body>
</html>
main.js
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()
будет генерировать исключение:
В примере «Повторные ошибки» показан результат вызова метода.
Повторные ошибки
Измените значение или результат обработчика и сравните порядок сообщений после запуска.
index.html
<html lang="ru">
<head>
<meta charset="UTF-8" />
<title>Повторные ошибки</title>
</head>
<body>
<p>Результат выполнения выводится в консоль.</p>
</body>
</html>
main.js
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():
В примере «Таймер без возврата обещания» показан результат вызова метода.
Таймер без возврата обещания
Измените значение или результат обработчика и сравните порядок сообщений после запуска.
index.html
<p>Результат выполнения выводится в консоль под примером.</p>
main.js
const promise = Promise.resolve('hello') // обещание успешно выполнено с заданным значением
promise
.then(function (val) {
// обработчик для успешного выполнения
return new Promise(function (resolve, reject) {
// возвращаем новое обещание
setTimeout(function () {
// вызываем функцию через указанное число миллисекунд
val += ' world' // изменяем полученное значение
console.log(1, val) // выводим значение в консоль
resolve(val) // изменяем состояние обещания на fulfilled (успешное выполнение)
}, 100)
})
})
.then(function (val) {
// обработчик для успешного выполнения
setTimeout(function () {
// вызываем функцию через указанное число миллисекунд
val += ' !!!' // изменяем полученное значение
console.log(2, val) // выводим значение в консоль
}, 100)
return val // возвращаем значение
})
.then(function (val) {
// обработчик для успешного выполнения
console.log(3, val) // выводим значение в консоль
})
// 1 "hello world"
// 3 "hello world"
// 2 "hello world !!!"
style.css
body {
margin: 16px;
font:
16px/1.5 system-ui,
sans-serif;
overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
max-width: 100%;
}
Нажмите ▶, чтобы запустить пример.
В этом примере мы инициализировали переменную, которая содержит объект
Promise, который мы вернули с использованием метода
resolve()
(обещание было успешно выполнено). С использованием метода then() мы добавили обработчики, вызываемые
когда объект
Promise
имеет состояние fulfilled (успешное выполнение).
Внутри первого обработчика then() мы возвращаем новое обещание, которое с использованием функции
setTimeout() через 100 мс вызовет метод
resolve(), который вернет обещание, которое было успешно выполнено.
Следующий метод then() принимает переданное значение, с использованием функции
setTimeout() через 100 мс изменяет это значение, выведет это значение в консоль, и возвратит
это значение. Здесь и кроется главный нюанс асинхронного выполнения, полученное значение будет передано сразу в
следующий обработчик не дожидаясь завершения выполнения функции setTimeout(), которая изменяет
впоследствии это значение.
Последний обработчик then() просто выводит в консоль полученное значение. В следующем примере мы
рассмотрим как вывести значение по порядку:
В примере «Ожидание возвращённого обещания» показан результат вызова метода.
Ожидание возвращённого обещания
Измените значение или результат обработчика и сравните порядок сообщений после запуска.
index.html
<p>Результат выполнения выводится в консоль под примером.</p>
main.js
const promise = Promise.resolve('hello') // обещание успешно выполнено с заданным значением
promise
.then(function (val) {
// обработчик для успешного выполнения
return new Promise(function (resolve, reject) {
// возвращаем новое обещание
setTimeout(function () {
// вызываем функцию через указанное число миллисекунд
val += ' world' // изменяем полученное значение
console.log(1, val) // выводим значение в консоль
resolve(val) // изменяем состояние обещания на fulfilled (успешное выполнение)
}, 100)
})
})
.then(function (val) {
// обработчик для успешного выполнения
return new Promise(function (resolve, reject) {
// возвращаем новое обещание
setTimeout(function () {
// вызываем функцию через указанное число миллисекунд
val += ' !!!' // изменяем полученное значение
console.log(2, val) // выводим значение в консоль
resolve(val) // изменяем состояние обещания на fulfilled (успешное выполнение)
}, 100)
})
})
.then(function (val) {
// обработчик для успешного выполнения
console.log(3, val) // выводим значение в консоль
})
// 1 "hello world"
// 2 "hello world !!!"
// 3 "hello world !!!"
style.css
body {
margin: 16px;
font:
16px/1.5 system-ui,
sans-serif;
overflow-wrap: anywhere;
}
img,
input,
select,
textarea,
button {
max-width: 100%;
}
Нажмите ▶, чтобы запустить пример.
По аналогии с предыдущим примером мы инициализировали переменную, которая содержит объект
Promise, который мы вернули с использованием метода
resolve()
(обещание было успешно выполнено), и с использованием метода then() добавили обработчики.
Как вы могли догадаться, в отличии от предыдущего примера нам достаточно во втором методе
then() вернуть обещание по аналогии с первым вызовом этого метода. В этом случае следующий
обработчик не будет вызван до тех пор пока предыдущее обещание не будет выполнено, или отклонено. Зная эти нюансы вы
сможете эффективно выполнять свою работу исходя из поставленной задачи.