Promise.prototype.then()

Метод then() вертає об'єкт Promise. Він приймає два аргументи: функції зворотного виклику для випадків успішного та неуспішного проміса.

Якщо один чи обидва аргументи пропущені, або надані не функції, тоді 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() вертає проміс. Ця функція
  // створює схожий 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 на основі промісів

Використання методу Function.prototype.bind() Reflect.apply (Reflect.apply()) для створення функції (що не скасовується) у стилі 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;
})();

Специфікації

Специфікація
ECMAScript (ECMA-262)
The definition of 'Promise.prototype.then' in that specification.

Сумісність з веб-переглядачами

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
then()Chrome Full support 32Edge Full support 12Firefox Full support 29IE No support NoOpera Full support 19Safari Full support 8WebView Android Full support 4.4.3Chrome Android Full support 32Firefox Android Full support 29Opera Android Full support 19Safari iOS Full support 8Samsung Internet Android Full support 2.0nodejs Full support 0.12

Legend

Full support  
Full support
No support  
No support

Див. також