Обещания имеют статус, они начинаются как ожидающие и могут рассчитывать на:
- выполнив это означает, что вычисление завершено успешно.
- отклонено означает, что вычисление не удалось.
Функции возврата обещаний никогда не должны выдаваться, вместо этого они должны возвращать отклонения. Функция возврата из обещания заставит вас использовать и a, } catch {
и a .catch
. Люди, использующие обещанные API-интерфейсы, не ожидают обещаний. Если вы не уверены, как работают асинхронные API в JS - сначала посмотрите этот ответ .
1. Загрузка DOM или другое одноразовое событие:
Таким образом, создание обещаний обычно означает указание того, когда они рассчитываются, то есть когда они переходят к выполненной или отклоненной фазе, чтобы указать, что данные доступны (и могут быть доступны с помощью .then
).
С современными реализациями обещаний, которые поддерживают Promise
конструктор как родные обещания ES6:
function load() {
return new Promise(function(resolve, reject) {
window.onload = resolve;
});
}
Затем вы будете использовать полученное обещание так:
load().then(function() {
// Do things after onload
});
С библиотеками, которые поддерживают отложенный (давайте используем здесь $ q для этого примера, но позже мы также будем использовать jQuery):
function load() {
var d = $q.defer();
window.onload = function() { d.resolve(); };
return d.promise;
}
Или с помощью jQuery-подобного API, перехватывая событие, происходящее один раз:
function done() {
var d = $.Deferred();
$("#myObject").once("click",function() {
d.resolve();
});
return d.promise();
}
2. Простой обратный вызов:
Эти API довольно распространены, так как ... обратные вызовы распространены в JS. Давайте посмотрим на общий случай наличия onSuccess
и onFail
:
function getUserData(userId, onLoad, onFail) { …
С современными реализациями обещаний, которые поддерживают Promise
конструктор как родные обещания ES6:
function getUserDataAsync(userId) {
return new Promise(function(resolve, reject) {
getUserData(userId, resolve, reject);
});
}
С библиотеками, которые поддерживают отложенный (давайте используем jQuery для этого примера здесь, но мы также использовали $ q выше):
function getUserDataAsync(userId) {
var d = $.Deferred();
getUserData(userId, function(res){ d.resolve(res); }, function(err){ d.reject(err); });
return d.promise();
}
jQuery также предлагает $.Deferred(fn)
форму, которая позволяет нам написать выражение, которое очень близко имитирует new Promise(fn)
форму, следующим образом:
function getUserDataAsync(userId) {
return $.Deferred(function(dfrd) {
getUserData(userId, dfrd.resolve, dfrd.reject);
}).promise();
}
Примечание: здесь мы используем тот факт, что jQuery deferred resolve
и reject
методы являются «отделяемыми»; то есть. они привязаны к экземпляру jQuery.Deferred (). Не все библиотеки предлагают эту функцию.
3. Обратный вызов стиля узла («nodeback»):
Обратные вызовы стиля узла (nodebacks) имеют определенный формат, где обратные вызовы всегда являются последним аргументом, а его первый параметр является ошибкой. Давайте сначала пообещаем одно вручную:
getStuff("dataParam", function(err, data) { …
Для того, чтобы:
function getStuffAsync(param) {
return new Promise(function(resolve, reject) {
getStuff(param, function(err, data) {
if (err !== null) reject(err);
else resolve(data);
});
});
}
С deferreds вы можете сделать следующее (давайте используем Q для этого примера, хотя Q теперь поддерживает новый синтаксис, который вы должны предпочесть ):
function getStuffAsync(param) {
var d = Q.defer();
getStuff(param, function(err, data) {
if (err !== null) d.reject(err);
else d.resolve(data);
});
return d.promise;
}
В общем, вам не следует слишком много обещать вручную, в большинстве библиотек обещаний, которые были разработаны с учетом Node, а также в собственных обещаниях в Node 8+ есть встроенный метод для обещания обратных узлов. Например
var getStuffAsync = Promise.promisify(getStuff); // Bluebird
var getStuffAsync = Q.denodeify(getStuff); // Q
var getStuffAsync = util.promisify(getStuff); // Native promises, node only
4. Целая библиотека с обратными вызовами в стиле узла:
Здесь нет золотого правила, вы обещаете их один за другим. Тем не менее, некоторые реализации обещаний позволяют вам делать это массово, например, в Bluebird, преобразование API обратной ноды в API обещаний так же просто, как:
Promise.promisifyAll(API);
Или с нативными обещаниями в Node :
const { promisify } = require('util');
const promiseAPI = Object.entries(API).map(([key, v]) => ({key, fn: promisify(v)}))
.reduce((o, p) => Object.assign(o, {[p.key]: p.fn}), {});
Ноты:
- Конечно, когда вы находитесь в
.then
обработчике, вам не нужно ничего обещать. Возврат обещания от .then
обработчика разрешит или отклонит значение этого обещания. Бросок из .then
обработчика также является хорошей практикой и отвергнет обещание - это знаменитое обещание безопасности броска.
- В реальном
onload
случае вы должны использовать, addEventListener
а не onX
.