promise 有状态,开始时是待定的,可以解决以下问题:
- fulfilled表示计算成功完成.
- rejected意味着计算失败.
promise 返回函数should never throw,则它们应该返回拒绝.从promise 返回函数抛出将迫使您同时使用} catch {
、and和.catch
.使用简化API的人们并不指望会抛出promise .如果您不确定异步API在JS中是如何工作的,请先使用see this answer.
1.DOM加载或其他一次性事件:
因此,创建promise 通常意味着指定它们何时结算——也就是说,当它们移动到满足或拒绝阶段时,表明数据可用(并且可以使用.then
访问).
通过支持Promise
个构造函数的现代promise实现,如本机ES6 promises:
function load() {
return new Promise(function(resolve, reject) {
window.onload = resolve;
});
}
然后,您可以按如下方式使用生成的promise :
load().then(function() {
// Do things after onload
});
对于支持deferred的库(在这里我们使用$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相当常见,因为Well…回调在JS中很常见.让我们看一下onSuccess
和onFail
的常见情况:
function getUserData(userId, onLoad, onFail) { …
通过支持Promise
个构造函数的现代promise实现,如本机ES6 promises:
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
方法是"可分离的"这一事实;也就是说,它们被绑定到jQuery的instance.延迟().并非所有LIB都提供此功能.
3. node 样式回调("nodeback"):
node 样式回调(NodeBack)有一种特定的格式,其中回调始终是最后一个参数,其第一个参数是错误.让我们先手动提示一个:
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现在支持新语法which you should prefer):
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 8+中考虑到Node和本机promise 而设计的Promise库都有一个内置的方法来代理nodeback.例如
var getStuffAsync = Promise.promisify(getStuff); // Bluebird
var getStuffAsync = Q.denodeify(getStuff); // Q
var getStuffAsync = util.promisify(getStuff); // Native promises, node only
4.带 node 样式回调的整库:
这里没有金科玉律,你一个接一个地promise .然而,一些promise实现允许您批量执行此操作,例如在Bluebird中,将nodeback API转换为promise API非常简单:
Promise.promisifyAll(API);
或每Node人中有native promises人:
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
.