Три способа понять промисы
Статья описывает три способа понимания промисов (обещаний). Ниже показан пример функции 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 кэшируется и передается слушателям событий, зарегистрированным после того, как промис был выполнен.