10月阅读周·JavaScript异步编程设计快速响应的网络应用:Promise对象的合并

举报
叶一一 发表于 2024/10/21 09:20:11 2024/10/21
【摘要】 背景去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。没有计划的阅读,收效甚微。新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。这个“玩法”虽然常见且板正,但是有效,已经坚持阅读九个月。已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScri...

背景

去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。

没有计划的阅读,收效甚微。

新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。

这个“玩法”虽然常见且板正,但是有效,已经坚持阅读九个月。

已读完书籍《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》、《WebKit技术内幕》、《前端架构:从入门到微前端》、《秒懂算法:用常识解读数据结构与算法》、《JavaScript权威指南》

当前阅读周书籍《JavaScript异步编程设计快速响应的网络应用》

Promise对象的合并

进度通知的存在并没有改变每个Promise对象的最终状态为已执行或已拒绝这一事实。(否则,Promise 对象将永远保持挂起状态。)但为什么要这样呢?为什么不让Promise对象随时变化成任意的状态,而偏偏只有这两种状态呢?

这样设计 Promise,其主要原因是程序员一直都在跟二进制打交道。我们这些码农非常清楚如何把1和0揉捏起来建成令人瞠目的逻辑之塔。Promise 如此强大的一个主要原因是,它允许我们把任务当成布尔量来处理。

假设我们正在播放一段演示视频,同时又在加载服务器上的一个游戏。我们希望这两件事一旦结束(对次序没有要求),就马上启动游戏。

  • 演示视频已经播放完毕。
  • 游戏已经加载完毕。

这两个进程各用一个Promise对象来表示,我们的任务就是在这两个Promise均已执行时启动游戏。我们如何做到这一点呢?

下面介绍jQuery的when方法:

var gameReadying = $.when(tutorialPromise, gameLoadedPromise);
gameReadying.done(startGame);

when相当于Promise执行情况的逻辑与运算符(AND)。一旦给定的所有Promise均已执行,就立即执行when方法产生的Promise对象;或者,一旦给定的任意一个Promise被拒绝,就立即拒绝 when 产生的Promise。

when方法的绝佳用例是合并多重Ajax调用。假设需要马上进行两次post 调用,而且要在这两次调用都成功时收到通知,这时就无需再为每次调用请求分别定义一个回调。

$.when($.post('/1', data1), $.post('/2', data2)).then(onPosted, onFailure);

调用成功时,when 可以访问下辖的各个成员 Promise对象的回调参数,不过这么做很复杂。这些回调参数会当作参数列表进行传递,传递的次序和成员Promise对象传递给 when 方法时一样。如果某个成员Promise对象提供多个回调参数,则这些参数会先转换成数组。

因此,要想根据赋予$.when 方法的所有成员 Promise对象获得全部回调参数,可能会写出像下面这样的代码(但笔者并不推荐这么做)。

$.when(promise1, promise2).done(function (promise1Args, promise2Args) {
  // ……
});

在这个例子中,如果执行promise1时用到了一个参数'complete',执行promise2时用到了3个参数(1、2、3),则promise1Args就是字符串'complete',promise2Args就是数组[1,2,3]。

虽然有可能,但如果不是绝对必要,我们不应该自行解析when回调的参数,相反应该直接向那些传递至 when 方法的成员Promise对象附加回调来收集相应的结果。

var serverData = {};
var getting1 = $.get('/1').done(function (result) {
  serverData['1'] = result;
});
var getting2 = $.get('/2').done(function (result) {
  serverData['2'] = result;
});
$.when(getting1, getting2).done(function () {
  // 获得的信息现在都已位于serverData……
});

函数的Promise用法

$.when 及其他能取用 Promise 对象的 jQuery 方法均支持传入非Promise 对象作为参数。这些非 Promise参数会被当成因相应参数位置已赋值而执行的Promise对象来处理。例如

$.when('foo')

会生成一个因赋值'foo'而立即执行的Promise对象。再譬如

var promise = $.Deferred().resolve('manchu');
$.when('test', promise);

会生成一个因赋值'test'和数组[1,2,3]而立即执行的 Promise 对象。(请记住,Deferred对象传递多个参数给resolve方法时,$.when会把这些参数转换成一个数组。)

这带来了一个问题:$.when 如何知道参数是不是 Promise对象呢?答案是:jQuery负责检查$.when的各个参数是否带有promise方法,如果有就使用该方法返回的值。Promise对象的promise方法会直接返回自身。

jQuery 对象也可以有 promise 方法,这意味着$.when 方法强行将那些带 promise 方法的 jQuery 对象转换成了jQuery动画版Promise对象。因此,如果想生成一个在抓取某些数据且已完成#loading 动画之后执行的 Promise对象,只需写下下面这样的代码:

var fetching = $.get('/myData');
$.when(fetching, $('#loading'));

只是请记住,必须要在动画开始之后再执行$.when 生成的那个Promise对象。如果#loading 的动画队列为空,则立即执行相应的Promise对象。

总结

Promise 对象的逻辑合并技术有一个最常见的用例:判定一组异步任务何时完成。

when相当于Promise执行情况的逻辑与运算符(AND)。一旦给定的所有Promise均已执行,就立即执行when方法产生的Promise对象;或者,一旦给定的任意一个Promise被拒绝,就立即拒绝 when 产生的Promise。


作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏️ | 留言📝

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。