Promises 有状态,它们开始时处于待定状态,并且可以解决:
Promise 返回函数不应该 throw,它们应该返回拒绝。从Promise返回函数中抛出将迫使您同时使用 a} catch {
和a .catch
。使用promisified API 的人不期望Promise会抛出。如果您不确定异步 API 如何在 JS 中工作 - 请先查看此答案。
1. DOM 加载或其他一次性事件:
因此,创建Promise通常意味着指定它们何时解决——这意味着它们何时进入已完成或拒绝阶段以指示数据可用(并且可以使用 访问.then
)。
使用支持Promise
构造函数的现代 Promise 实现,如原生 ES6 Promise:
function load() {
return new Promise(function(resolve, reject) {
window.onload = resolve;
});
}
然后,您将像这样使用生成的 promise:
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
and的常见情况onFail
:
function getUserData(userId, onLoad, onFail) { …
使用支持Promise
构造函数的现代 Promise 实现,如原生 ES6 Promise:
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 延迟resolve
和reject
方法是“可分离的”;IE。它们绑定到jQuery.Deferred()的实例。并非所有库都提供此功能。
3.节点样式回调(“nodeback”):
节点样式回调 (nodebacks) 具有特定格式,其中回调始终是最后一个参数,其第一个参数是错误。让我们首先手动Promise一个:
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);
});
});
}
使用延迟,您可以执行以下操作(让我们在此示例中使用 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;
}
一般来说,你不应该过多地手动Promise事情,大多数在设计时考虑到 Node 的Promise库以及 Node 8+ 中的原生Promise都有一个内置的方法来Promise节点背。例如
var getStuffAsync = Promise.promisify(getStuff); // Bluebird
var getStuffAsync = Q.denodeify(getStuff); // Q
var getStuffAsync = util.promisify(getStuff); // Native promises, node only
4. 带有节点样式回调的完整库:
这里没有黄金法则,你一一Promise。但是,一些 promise 实现允许您批量执行此操作,例如在 Bluebird 中,将 nodeback API 转换为 promise API 非常简单:
Promise.promisifyAll(API);
或者在Node 中使用本机Promise:
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
处理程序中时,您不需要Promise事情。从.then
处理程序返回Promise将使用该Promise的值来解决或拒绝。从.then
处理程序中抛出也是一种很好的做法,并且会拒绝Promise - 这就是著名的Promise抛出安全。
- 在实际
onload
情况下,您应该使用addEventListener
而不是onX
.