Домой Новости Три способа понять промисы

Три способа понять промисы

4269
Три способа понять промисы
Три способа понять промисы

Три способа понять промисы

Статья описывает три способа понимания промисов (обещаний). Ниже показан пример функции asyncFunc(), основанной на промисах:

function asyncFunc() {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve('DONE'), 100);
    });
}
asyncFunc()
.then(x => console.log('Result: '+x));
    
// Output:
// Result: DONE

Так что такое промис?

  • Технически, вызов asyncFunc() — это блокирующий вызов функции.
  • Промис — это одновременно и контейнер для значения и эмиттер событий.

1. Вызов Promise-based функции является блокирующим

function asyncFunc() {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve('DONE'), 100);
    });
}
async function main() {
    const x = await asyncFunc(); // (A)
    console.log('Result: '+x);
    
    // Same as:
    // asyncFunc()
    // .then(x => console.log('Result: '+x));
}
main();

Функция main() — это асинхронная функция. Ее тело показывает, что происходит концептуально — как мы обычно думаем об асинхронных вычислениях:

Строка (А): Ждет пока выполнится asyncFunc().
Строка (B): Выводит результат x в консоль.

До ECMAScript 6 и генераторов вы не могли приостанавливать и возобновлять код, поэтому с помощью промисов вы помещаете все, что происходит после возобновления кода, в коллбэк. Вызов этого коллбэка аналогичен возобновлению кода.

2. Промис — это контейнер для асинхронно возвращаемого значения

Если функция возвращает промис, то этот промис похож на пустое значение, которое функция (как правило) в конечном итоге заполнит результатом, как только вычислит его. Вы можете смоделировать простую версию этого процесса через массив:

function asyncFunc() {
    const blank = [];
    setTimeout(() => blank.push('DONE'), 100);
    return blank;
}
const blank = asyncFunc();
// Wait until the value has been filled in
setTimeout(() => {
    const x = blank[0]; // (A)
    console.log('Result: '+x);
}, 200);

С промисами вы не получаете доступ к конечным значениям с помощью [] (как в строке (А)), а используете метод then() и функцию обратного вызова.

3. Промис — это эмиттер событий

Другой способ взглянуть на промис — как на объект, который генерирует события.

function asyncFunc() {
    const eventEmitter = { success: [] };
    setTimeout(() => { // (A)
        for (const handler of eventEmitter.success) {
            handler('DONE');
        }
    }, 100);
    return eventEmitter;
}
asyncFunc()
.success.push(x => console.log('Result: '+x)); // (B)

Регистрация слушателя событий (строка (B)) может быть выполнена после вызова asyncFunc(), так как коллбэк, переданный в setTimeout() (строка (A)) выполняется асинхронно, после того, как выполнится сама функция.

Обычные эмиттеры событий порождают несколько событий, начиная с момента регистрации.

Промисы же возвращают только одно значение и имеют встроенную защиту от слишком поздней регистрации: результат Promise кэшируется и передается слушателям событий, зарегистрированным после того, как промис был выполнен.

источник

original