Статьи

Шесть вещей, которые вы, возможно, не знаете об обещаниях

Обещания — это простая концепция, и даже если у вас не было возможности их использовать, вы, возможно, уже прочитали их. Они представляют собой ценную конструкцию, позволяющую структурировать асинхронный код в более удобочитаемом виде, а не в виде беспорядка вложенных анонимных функций. В этой статье рассматриваются шесть вещей, которые вы можете не знать об обещаниях.

Перед тем, как погрузиться в список, давайте кратко напомним, как выглядят обещания JavaScript:

var p = new Promise(function(resolve, reject) { resolve("hello world"); }); p.then(function(str) { alert(str); }); 

1. then() возвращает разветвленное обещание

В чем разница между следующими двумя блоками кода?

 // Exhibit A var p = new Promise(/*...*/); p.then(func1); p.then(func2); 
 // Exhibit B var p = new Promise(/*...*/); p.then(func1) .then(func2); 

Если вы думаете, что оба блока кода эквивалентны, вы можете подумать, что обещания — это не более чем одномерные массивы обратных вызовов. Однако на самом деле это не так. Каждый вызов then() возвращает раздвоенное обещание. Таким образом, в Приложении A, если func1() выдает исключение, func2() все равно будет вызываться как обычно.

В Приложении B, если func1() выдает исключение, func2() вызываться не будет, поскольку первый вызов then() возвратил новое обещание, которое было отклонено из-за исключения в func1() . В результате func2() пропускается.

Вывод: обещания могут быть разбиты на несколько путей, например, сложная блок-схема.

2. Обратные вызовы должны пройти результаты

Что предупреждает вас, когда вы запускаете следующий код?

 var p = new Promise(function(resolve, reject) { resolve("hello world"); }); p.then(function(str) {}) .then(function(str) { alert(str); }); 

Предупреждение во втором then() ничего не отображает. Это связано с тем, что обратные вызовы в контексте обещаний — это не столько обратные вызовы, сколько преобразователи результатов. Обещание ожидает, что ваш обратный вызов либо возвратит тот же результат, либо замену, которая затем будет передана следующему обратному вызову.

Эта идея похожа на использование адаптеров для преобразования результата, как показано в следующем примере.

 var feetToMetres = function(ft) { return ft*12*0.0254 }; var p = new Promise(/*...*/); p.then(feetToMetres) .then(function(metres) { alert(metres); }); 

3. Только исключения из предыдущих уровней

В чем разница между этими двумя кодовыми блоками:

 // Exhibit A new Promise(function(resolve, reject) { resolve("hello world"); }) .then( function(str) { throw new Error("uh oh"); }, undefined ) .then( undefined, function(error) { alert(error); } ); 
 // Exhibit B new Promise(function(resolve, reject) { resolve("hello world"); }) .then( function(str) { throw new Error("uh oh"); }, function(error) { alert(error); } ); 

В Приложении A, когда исключение выдается в первом then() , оно перехватывается во втором then() и выдается предупреждение «ооо». Это следует правилу, что ловятся только исключения из предыдущих уровней.

В Приложении B обратный вызов и обратный вызов с ошибками находятся на одном уровне, то есть, когда в обратном вызове возникает исключение, оно не будет перехвачено. На самом деле обратный вызов ошибки приложения B будет выполняться только в том случае, если обещание
в отклоненном состоянии или если само обещание выдает исключение.

4. Ошибки могут быть восстановлены из

Внутри обратного вызова ошибки, если вы не перезапустите ошибку, обещание будет предполагать, что вы восстановились после ошибки, и вернется к разрешенному состоянию. В следующем примере отображается сообщение «Я сохранен», поскольку обратный вызов ошибки в методе then() не перезапустил исключение.

 var p = new Promise(function(resolve, reject) { reject(new Error("pebkac")); }); p.then( undefined, function(error) { } ) .then( function(str) { alert("I am saved!"); }, function(error) { alert("Bad computer!"); } ); 

Обещания можно рассматривать как слои на луке. Каждый then() добавляет еще один слой к луку. Каждый слой представляет одно действие, которое может быть обработано. После того, как этот уровень закончен, предполагается, что результат зафиксирован и готов к следующему уровню.

5. Обещания могут быть приостановлены

То, что вы уже выполняете функцию then() , не означает, что вы не можете приостановить ее, чтобы сначала выполнить что-то еще. Чтобы приостановить текущее обещание или заставить его ждать завершения другого обещания, просто верните другое обещание изнутри then() .

 var p = new Promise(/*...*/); p.then(function(str) { if(!loggedIn) { return new Promise(/*...*/); } }) .then(function(str) { alert("Done."); }) 

В предыдущем примере кода предупреждение не будет отображаться, пока новое обещание не будет разрешено. Это удобный способ введения дополнительных зависимостей в существующий путь асинхронного кода. Например, вы можете обнаружить, что время сеанса пользователя истекло, и вы можете инициировать вторичный вход в систему, прежде чем продолжить с предыдущим путем кода.

6. Решенные обещания не выполняются немедленно

Что предупреждает вас, когда вы запускаете следующий код?

 function runme() { var i = 0; new Promise(function(resolve) { resolve(); }) .then(function() { i += 2; }); alert(i); } 

Вы можете подумать, что это предупредит 2, поскольку обещание разрешается немедленно, а функция then() выполняется немедленно (синхронно). Однако спецификация обещания требует, чтобы все вызовы были принудительно асинхронными, чтобы быть единообразными. Следовательно, оповещение вызывается до изменения значения i .

Ссылки:
Загрузите различные реализации API Promise / A + .