10月阅读周·JavaScript异步编程设计快速响应的网络应用:生成Promise对象篇
背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效,已经坚持阅读九个月。
已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》、《WebKit技术内幕》、《前端架构:从入门到微前端》、《秒懂算法:用常识解读数据结构与算法》、《JavaScript权威指南》。
当前阅读周书籍:《JavaScript异步编程设计快速响应的网络应用》。
生成Promise对象
假设我们提示用户应敲击 Y键或 N键。为此要做的第一件事就是生成一个$.Deferred实例以代表用户做出的决定。
var promptDeferred = new $.Deferred();
promptDeferred.always(function () {
console.log('A choice was made:');
});
promptDeferred.done(function () {
console.log('Starting game……');
});
promptDeferred.fail(function () {
console.log('No game today.');
});
(注:always 关键字仅适用于jQuery 1.6+。)
大家可能会奇怪:为什么本节叫做“生成 Promise 对象”,却要生成一个Deferred(延迟)实例?别担心,Deferred就是Promise!更准确地说,Deferred是Promise的超集,它比Promise多了一项关键特性:可以直接触发。纯Promise实例只允许添加多个调用,而且必须由其他什么东西来触发这些调用。
使用resolve(执行)方法和reject(拒绝)方法均可触发Deferred对象。
$('#playGame')
.focus()
.on('keypress', function (e) {
var Y = 121,
N = 110;
if (e.keyCode === Y) {
promptDeferred.resolve();
} else if (e.keyCode === N) {
promptDeferred.reject();
} else {
return false; // 这里的Deferred 对象保持着挂起状态
}
});
输出:
A choice was made:
Starting game...
大家看懂是怎么回事了吗?执行了Deferred(即对Deferred对象调用了resolve方法)之后,即运行该对象的always(恒常)回调和done(已完成)回调。(会按照绑定回调的次序来运行回调,这可不是巧合哦!)
刷新页面,敲击N键。
A choice was made:
Noe game today.
这样,拒绝了Deferred(即对Deferred对象调用了reject方法)之后,即运行该对象的 always 回调和 fail(失败)回调。注意,始终会按照绑定回调的次序来运行回调。如果最后绑定的是always回调,则控制台的输出行顺序会反过来。
再试着反复敲击 Y键和 N键。第一次做出选择之后,就再也没有反应了!这是因为Promise只能执行或拒绝一次,之后就失效了。我们断言,Promise 对象会一直保持挂起状态,直到被执行或拒绝。对Promise对象调用state(状态)方法,可以查看其状态是"pending"、"resolved",还是"rejected"。(到 jQuery 1.7才添加了 state 方法,此前的版本使用的是isResolved和isRejected。)
如果正在进行的一次性异步操作的结果可以笼统地分成两种(如成功/失败,或接受/拒绝),则生成Deferred对象就能直观地表达这次任务。
成纯Promise对象
CommonJS的Promises/A规范及其实现中使用了一种更合理的说法:Promise对象已履行(关键字为fulfill)或已拒绝,这两种情况都称Promise已执行。3.7节会对此作进一步阐述。
var promptPromise = promptDeferred.promise();
promptPromise 只是 promptDeferred 对象的一个没有resolve/reject方法的副本。我们把回调绑定至Deferred或其下辖的Promise并无不同,因为这两个对象本质上分享着同样的回调。它们也分享着同样的state(返回的状态值为"pending"、"resolved"或"rejected")。这意味着,对同一个Deferred对象生成多个Promise对象是毫无意义的。事实上,jQuery给出的只不过是同一个对象。
var promise1 = promptDeferred.promise();
var promise2 = promptDeferred.promise();
console.log(promise1 === promise2); // true
而且,对一个纯Promise对象再调用promise方法,产生的只不过是一个指向相同对象的引用。
console.log(promise1 === promise1.promise()); // true
总结
使用promise方法的唯一理由就是“封装”。如果传递promptPromise对象,但保留promptDeferred 对象为己所用,则可以肯定的是,除非是你自己想触发那些回调,否则任何回调都不会被触发。
每个Deferred对象都含有一个Promise对象,而每个Promise对象都代表着一个 Deferred对象。有了 Deferred对象,就可以控制其状态,而有了纯 Promise 对象,只能读取其状态及附加回调。
作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)