Promise

Об'єкт Promise відображає остаточне завершення (або неуспіх) асинхронної операції та значення, яке вона повертає.

Щоб дізнатись, як працюють проміси та як їх можна використовувати, радимо вам спочатку прочитати статтю Використання промісів.

Опис

Promise - це проксі для значення, яке може бути невідомим на момент створення проміса. Це дозволяє зв'язувати обробники з кінцевим успішним значенням чи причиною неуспіху асинхронних дій. Таким чином, асинхронні методи повертають значення, як синхронні методи: замість того, щоб негайно повернути кінцеве значення, асинхронний метод повертає проміс, щоб надати значення в певний момент у майбутньому.

Об'єкт Promise може знаходитись в одному з цих станів:

  • pending (у стані очікування): початковий стан, ані виконаний, ані відхилений.
  • fulfilled (виконаний): означає, що операція завершилася вдало.
  • rejected (відхилений): означає, що операція була неуспішною.

Проміс у стані очікування може стати або виконаним (fulfilled) з певним значенням, або відхиленим (rejected) з причиною відхилення (помилкою). Коли щось із цього відбувається, викликаються відповідні обробники, що ставляться в чергу методом об'єкта then. (Якщо проміс вже був виконаний чи відхилений ще до моменту приєднання відповідного обробника, то обробник буде викликаний, таким чином не відбувається "стану гонки" між завершенням асинхронної операції та приєднанням її обробників)

Оскільки методи Promise.prototype.then() та Promise.prototype.catch() повертають проміси, їх можна з'єднувати в ланцюжки.

Не варто плутати з: Декілька інших мов мають механізми лінивих обчислень та відкладених розрахунків, які також називаються "promises" - наприклад, Scheme. Проміси у JavaScript відображають процеси, які вже відбуваються і які можуть бути з'єднані в ланцюги з функціями зворотного виклику. Якщо вам потрібне ліниве обчислення виразу, розгляньте стрілкові функції без аргументів: f = () => вираз для створення лінивого виразу, та f() для обчислення.

Заувага: Проміс називають встановленим (settled), якщо він або виконаний, або відхилений, але не знаходиться у стані очікування. Ви також почуєте термін вирішений (resolved) щодо промісів - він означає, що проміс встановлений, або ж "зафіксований", щоб відповідати стану іншого проміса. Стаття States and Fates містить більше подробиць щодо термінології промісів.

Конструктор

Promise()
Конструктор переважно використовується для загортання функцій, які самі не підтримують проміси.

Властивості

Promise.length
Значення довжини, завжди дорівнює 1 (кількість аргументів конструктора).
Promise.prototype
Прототип для конструктора Promise.

Методи

Promise.all(iterable)
Чекає, доки усі проміси не будуть вирішені, або поки будь-який з промісів не буде відхилений.
Якщо повернений проміс вирішується, він вирішується із сукупним масивом значень вирішених промісів, у тому ж порядку, в якому вони визначені в ітерабельному об'єкті промісів.
В разі відхилення, він відхиляється з причиною з першого відхиленого проміса у ітерабельному об'єкті.
Promise.allSettled(iterable)
Чекає, доки усі проміси не будуть встановлені (кожен має бути або вирішений, або відхилений).
Повертає проміс, який вирішується після того, як усі надані проміси були або вирішені, або відхилені, з масивом об'єктів, які описують результат кожного проміса.
Promise.race(iterable)
Чекає, доки будь-який з промісів не буде або вирішений, або відхилений.
Якщо повернений проміс вирішений, він вирішується зі значенням першого проміса з ітерабельного об'єкта, який був вирішений.
Якщо він відхилений, він відхиляється з причиною першого відхиленого проміса.
Promise.reject(reason)
Повертає новий об'єкт Promise, відхилений з наданою причиною.
Promise.resolve(value)
Повертає новий об'єкт Promise, який вирішується з наданим значенням. Якщо значенням є промісоподібний об'єкт (такий, що має метод then), то повернений проміс буде його "дотримуватись", приймаючи його кінцевий стан; у іншому випадку повернений проміс буде виконаний з наданим значенням.
Загалом, якщо ви не знаєте, є значення промісом чи ні, використайте Promise.resolve(value) та працюйте з поверненим значенням як з промісом.

Прототип Promise

Властивості

Promise.prototype.constructor
Вертає функцію, яка створила прототип екземпляра. Це за замовчуванням функція Promise.

Методи

Promise.prototype.catch()
Додає до проміса функцію зворотного виклику для обробки відхилення та вертає новий проміс, що вирішується з поверненим значенням цієї функції, коли вона викликається, або з початковим значенням виконання, якщо проміс, навпаки, виконається.
Promise.prototype.then()
Додає до проміса обробники виконання та відхилення та вертає новий проміс, що вирішується з поверненим значенням обробника, який викликався, або з початковим встановленим значенням, якщо проміс не оброблявся (тобто, якщо відповідний обробник onFulfilled чи onRejected не є функцією).
Promise.prototype.finally()
Додає до проміса обробник та вертає новий проміс, який вирішується, коли вирішується початковий проміс. Обробник викликається, коли проміс встановлений, тобто, або виконаний, або відхилений.

Приклади

Базовий приклад

let myFirstPromise = new Promise((resolve, reject) => {
  // Викликаємо resolve(...), коли те, що ми робили асинхронно, успішно виконалось, і reject(...), якщо неуспішно.
  // В цьому прикладі ми використовуємо setTimeout(...) для симуляції асинхронного коду.
  // В житті ви, ймовірно, використовуватиме щось на кшталт XHR або HTML5 API.
  setTimeout( function() {
    resolve("Успіх!")  // Є! Все пройшло добре!
  }, 250)
}) 

myFirstPromise.then((successMessage) => {
  // successMessage - це те, що ми передаємо у наведену вище функцію resolve(...).
  // Це не обов'язково має бути рядок, але, якщо це повідомлення про успіх, то, мабуть, це буде він.
  console.log("Є! " + successMessage)
});

Ускладнений приклад

Цей маленький приклад демонструє механізм об'єкта Promise. Метод testPromise() викликається кожний раз, коли натискається кнопка <button>. Він створює проміс, який буде виконаний з використанням window.setTimeout() з лічильником проміса (число, що стартує від 1) кожні 1-3 секунди, у випадковому порядку. Конструктор Promise() використовується для створення проміса.

Виконання проміса логується просто, виконанням зворотного виклику через p1.then(). Кілька логів демонструють, як синхронна частина методу відокремлюється від асинхронного завершення проміса.

'use strict';
var promiseCount = 0;

function testPromise() {
    var thisPromiseCount = ++promiseCount;

    var log = document.getElementById('log');
    log.insertAdjacentHTML('beforeend', thisPromiseCount +
        ') Запуск (<small>Синхронний код запущено</small>)<br/>');

    // Створюємо новий проміс: ми передаємо лічильник цього проміса, починаючи з 1 (після очікування 3с)
    var p1 = new Promise(
        // Функція вирішення викликається з можливістю вирішити або
        // відхилити проміс
        function(resolve, reject) {
            log.insertAdjacentHTML('beforeend', thisPromiseCount +
                ') Запуск проміса (<small>Асинхронний код запущено</small>)<br/>');
            // Це лише приклад для створення асинхронності
            window.setTimeout(
                function() {
                    // Ми виконуємо проміс!
                    resolve(thisPromiseCount);
                }, Math.random() * 2000 + 1000);
        }
    );

    // Визначаємо, що робити, коли проміс вирішено/виконано, викликом then(),
    // а метод catch() визначає, що робити, якщо проміс відхилено.
    p1.then(
        // Залогувати значення виконання
        function(val) {
            log.insertAdjacentHTML('beforeend', val +
                ') Проміс виконано (<small>Асинхронний код завершений</small>)<br/>');
        })
    .catch(
        // Залогувати причину відхилення
        function(reason) {
            console.log('Обробити тут відхилений проміс ('+reason+').');
        });

    log.insertAdjacentHTML('beforeend', thisPromiseCount +
        ') Проміс створено (<small>Синхронний код завершений</small>)<br/>');
}

Цей приклад починається натисканням кнопки. Вам потрібен переглядач, що підтримує об'єкти Promise. Натиснувши кнопку кілька разів за короткий відрізок часу, ви навіть побачите, як різні проміси виконуються один після іншого.

Завантаження зображення за допомогою XHR

Інший простий приклад використання об'єктів Promise та XMLHttpRequest - для завантаження зображення - доступний у репозиторії promise-test на MDN GitHub. Ви також можете побачити його в дії. Кожний крок супроводжується коментарями та дозволяє відслідкувати архітектуру Promise та XHR.

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

Specification
ECMAScript (ECMA-262)
The definition of 'Promise' in that specification.

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

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
PromiseChrome 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
Promise() constructorChrome Full support 32Edge Full support 12Firefox Full support 29
Notes
Full support 29
Notes
Notes Constructor requires a new operator since version 37.
IE No support NoOpera Full support 19Safari Full support 8
Notes
Full support 8
Notes
Notes Constructor requires a new operator since version 10.
WebView Android Full support 4.4.3Chrome Android Full support 32Firefox Android Full support 29
Notes
Full support 29
Notes
Notes Constructor requires a new operator since version 37.
Opera Android Full support 19Safari iOS Full support 8
Notes
Full support 8
Notes
Notes Constructor requires a new operator since version 10.
Samsung Internet Android Full support 2.0nodejs Full support 0.12
Notes
Full support 0.12
Notes
Notes Constructor requires a new operator since version 4.
all()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
allSettled()Chrome Full support 76Edge Full support 79Firefox Full support 71IE No support NoOpera Full support 63Safari Full support 13WebView Android Full support 76Chrome Android Full support 76Firefox Android No support NoOpera Android Full support 54Safari iOS Full support 13Samsung Internet Android No support Nonodejs Full support 12.9.0
catch()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
finally()Chrome Full support 63Edge Full support 18Firefox Full support 58IE No support NoOpera Full support 50Safari Full support 11.1WebView Android Full support 63Chrome Android Full support 63Firefox Android Full support 58Opera Android Full support 46Safari iOS Full support 11.3Samsung Internet Android Full support 8.0nodejs Full support 10.0.0
race()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
reject()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
resolve()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
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
See implementation notes.
See implementation notes.

Див. також