Promise.prototype.then()
Метод then()
вертає об'єкт Promise
. Він приймає два аргументи: функції зворотного виклику для випадків успішного та неуспішного проміса.
The source for this interactive example is stored in a GitHub repository. If you'd like to contribute to the interactive examples project, please clone https://github.com/mdn/interactive-examples and send us a pull request.
The source for this interactive demo is stored in a GitHub repository. If you'd like to contribute to the interactive demo project, please clone https://github.com/mdn/interactive-examples and send us a pull request.
Якщо один чи обидва аргументи пропущені, або надані не функції, тоді then
не матиме обробників, але не згенерує помилок. Якщо Promise
, на якому викликається then
, приймає стан (виконання
або відхилення
), для якого then
не має обробника, новий Promise
створюється без додаткових обробників, просто приймаючи кінцевий стан оригінального проміса, на якому було викликано then
.
Синтаксис
p.then(onFulfilled[, onRejected]); p.then(value => { // виконання }, reason => { // відхилення });
Параметри
onFulfilled
OptionalФункція
, що викликається, якщоPromise
виконано. Ця функція має один аргумент,значення виконання
. Якщо це не функція, вона внутрішньо замінюється на функцію "Identity" (вона повертає отриманий аргумент).onRejected
OptionalФункція
, що викликається, якщоPromise
відхилено. Ця функція має один аргумент,причина відхилення
. Якщо це не функція, вона внутрішньо замінюється на функцію "Thrower" (вона викидає помилку, яку отримала в якості аргумента).
Значення, що повертається
Як тільки проміс
був виконаний або відхилений, відповідна функція-обробник (onFulfilled
або onRejected
) буде викликана асинхронно (запланована у активному циклі потоку). Поведінка функції-обробника відповідає спеціальному набору правил. Якщо функція-обробник:
- вертає значення, проміс, повернений
then
, вирішується з поверненим значенням в якості його значення. - не вертає нічого, проміс, повернений
then
, вирішується зі значеннямundefined
. - викидає помилку, проміс, повернений
then
, відхиляється з викинутою помилкою в якості значення. - вертає вже виконаний проміс, то проміс, повернений
then
, виконується зі значенням цього проміса в якості свого значення. - вертає вже відхилений проміс, то проміс, повернений
then
, відхиляється зі значенням цього проміса в якості свого значення. - вертає інший проміс у стані очікування, вирішення/відхилення проміса, поверненого
then
, буде результатом вирішення/відхилення проміса, поверненого обробником. Також, вирішене значення проміса, поверненогоthen
, буде тим самим, що й вирішене значення проміса, поверненого обробником.
Наступний приклад демонструє асинхронність методу then
.
// при використанні вирішеного проміса блок 'then' буде негайно запущений,
// але його обробники запустяться асинхронно, як демонструє console.log
const resolvedProm = Promise.resolve(33);
let thenProm = resolvedProm.then(value => {
console.log("Це запускається після завершення головного стеку. Отримане й повернене значення: " + value);
return value;
});
// негайне логування значення thenProm
console.log(thenProm);
// використовуючи setTimeout, ми можемо відкласти виконання функції, поки стек не стане порожнім
setTimeout(() => {
console.log(thenProm);
});
// порядок логування:
// Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
// "Це запускається після завершення головного стеку. Отримане й повернене значення: 33"
// Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 33}
Опис
Оскільки методи then
та Promise.prototype.catch()
вертають проміси, їх можна поєднувати в ланцюги — ця операція зветься композицією.
Приклади
Використання метода then
var p1 = new Promise((resolve, reject) => {
resolve('Успіх!');
// або
// reject(new Error("Помилка!"));
});
p1.then(value => {
console.log(value); // Успіх!
}, reason => {
console.error(reason); // Помилка!
});
Ланцюгування
Метод then
вертає об'єкт Promise
, що дозволяє використовувати ланцюгування.
Якщо функція, передана у then
в якості обробника, вертає об'єкт Promise
, аналогічний об'єкт Promise
буде переданий у наступний then
ланцюга методів. Наведений нижче фрагмент імітує асинхронний код функцією setTimeout
.
Promise.resolve('ква')
// 1. Отримати "ква", приєднати "драт" та вирішити це для наступного then
.then(function(string) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
string += 'драт';
resolve(string);
}, 1);
});
})
// 2. отримати "квадрат", призначити функцію зворотного виклику для обробки цього рядка
// та вивести його на консоль, але не раніше повернення необробленого рядка
// string у наступний then
.then(function(string) {
setTimeout(function() {
string += 'ура';
console.log(string);
}, 1)
return string;
})
// 3. вивести допоміжні повідомлення щодо того, як виконується код в цьому розділі,
// раніше, ніж рядок string буде оброблений імітованим асинхронним кодом у
// попередньому блоці then.
.then(function(string) {
console.log("Останній Then: йой... ми не створили та не повернули екземпляр проміса " +
"у попередньому then, тому послідовність може бути трохи " +
"несподіваною");
// Зауважте, що `string` не матиме частини 'ура' в цій точці. Це тому,
// що ми імітували його асинхронне виконання за допомогою функції setTimeout
console.log(string);
});
// порядок виведення:
// Останній Then: йой... ми не створили та не повернули екземпляр проміса у попередньому then, тому послідовність може бути трохи несподіваною
// квадрат
// квадратура
Коли значення просто повертається з обробника then
, він поверне Promise.resolve(<значення, повернене обробником, що викликався>)
.
var p2 = new Promise(function(resolve, reject) {
resolve(1);
});
p2.then(function(value) {
console.log(value); // 1
return value + 1;
}).then(function(value) {
console.log(value + ' - Синхронне значення працює');
});
p2.then(function(value) {
console.log(value); // 1
});
Виклик then
поверне відхилений проміс, якщо функція викидає помилку або повертає відхилений проміс.
Promise.resolve()
.then(() => {
// Змушує .then() повернути відхилений проміс
throw new Error('О, ні!');
})
.then(() => {
console.log('Не викликається.');
}, error => {
console.error('Викликано функцію onRejected: ' + error.message);
});
У всіх інших випадках повертається вирішений об'єкт Promise. У наступному прикладі перший then()
поверне значення 42
, загорнуте у вирішений проміс, незважаючи на те, що попередній проміс ланцюжка був відхилений.
Promise.reject()
.then(() => 99, () => 42) // onRejected вертає 42, обгорнуте у вирішений Promise
.then(solution => console.log('Вирішений зі значенням ' + solution)); // Вирішений зі значенням 42
На практиці часто бажано перехоплювати відхилені проміси, як продемонстровано нижче, а не використовувати синтаксис then
для двох випадків.
Promise.resolve()
.then(() => {
// Змушує .then() повернути відхилений проміс
throw new Error('О, ні!');
})
.catch(error => {
console.error('Викликано функцію onRejected: ' + error.message);
})
.then(() => {
console.log("Мене завжди викликають, навіть якщо проміс попереднього then відхилено");
});
Ви також можете використати ланцюгування, щоб реалізувати функцію з API на промісах, на основі іншої такої функції.
function fetch_current_data() {
// API fetch (en-US)() вертає проміс. Ця функція
// створює схожий API, крім того, що над значенням
// виконанного проміса цієї функції виконується
// більше дій.
return fetch('current-data.json').then(response => {
if (response.headers.get('content-type') != 'application/json') {
throw new TypeError();
}
var j = response.json();
// можливо, зробити щось із j
return j; // значення виконання, що надається користувачу
// fetch_current_data().then()
});
}
Якщо onFulfilled
вертає проміс, повернене значення then
буде вирішене чи відхилене промісом.
function resolveLater(resolve, reject) {
setTimeout(function() {
resolve(10);
}, 1000);
}
function rejectLater(resolve, reject) {
setTimeout(function() {
reject(new Error('Помилка'));
}, 1000);
}
var p1 = Promise.resolve('ква');
var p2 = p1.then(function() {
// Повернути тут проміс, який буде вирішений зі значенням 10 через 1 секунду
return new Promise(resolveLater);
});
p2.then(function(v) {
console.log('вирішений', v); // "вирішений", 10
}, function(e) {
// не викликається
console.error('відхилений', e);
});
var p3 = p1.then(function() {
// Повернути тут проміс, що відхилятиметься з помилкою 'Помилка' через 1 секунду
return new Promise(rejectLater);
});
p3.then(function(v) {
// не викликається
console.log('вирішений', v);
}, function(e) {
console.error('відхилений', e); // "відхилений", 'Помилка'
});
Поліфіл у стилі window.setImmediate (en-US) на основі промісів
Використання методу Function.prototype.bind()
Reflect.apply
(Reflect.apply()
(en-US)) для створення функції (що не скасовується) у стилі setImmediate.
const nextTick = (() => {
const noop = () => {}; // буквально
const nextTickPromise = () => Promise.resolve().then(noop);
const rfab = Reflect.apply.bind; // (thisArg, fn, thisArg, [...args])
const nextTick = (fn, ...args) => (
fn !== undefined
? Promise.resolve(args).then(rfab(null, fn, null))
: nextTickPromise(),
undefined
);
nextTick.ntp = nextTickPromise;
return nextTick;
})();
Специфікації
Сумісність з веб-переглядачами
BCD tables only load in the browser
To contribute to this compatibility data, please write a pull request against this repository: https://github.com/mdn/browser-compat-data.