图解 Promise 实现原理(二)—— Promise 链式调用
本文首发于 vivo互联网技术 微信公众号
链接: https://mp.weixin.qq.com/s/Xz2bGaLxVL4xw1M2hb2nJQ
作者:Morrain
很多同学在学习 Promise 时,知其然却不知其所以然,对其中的用法理解不了。本系列文章由浅入深逐步实现 Promise,并结合流程图、实例以及动画进行演示,达到深刻理解 Promise 用法的目的。
本系列文章有如下几个章节组成:
- 图解 Promise 实现原理(一)—— 基础实现
- 图解 Promise 实现原理(二)—— Promise 链式调用
- 图解 Promise 实现原理(三)—— Promise 原型方法实现
- 图解 Promise 实现原理(四)—— Promise 静态方法实现
一、前言
上一节中,实现了 Promise 的基础版本:
但链式调用,只是在 then 方法中 return 了 this,使得 Promise 实例可以多次调用 then 方法,但因为是同一个实例,调用再多次 then 也只能返回相同的一个结果,通常我们希望的链式调用是这样的:
每个 then 注册的 onFulfilled 都返回了不同的结果,层层递进,很明显在 then 方法中 return this 不能达到这个效果。引入真正的链式调用,then 返回的一定是一个新的Promise实例。
真正的链式 Promise 是指在当前 Promise 达到 fulfilled 状态后,即开始进行下一个 Promise(后邻 Promise)。那么我们如何衔接当前 Promise 和后邻 Promise 呢?(这是理解 Promise 的难点,我们会通过动画演示这个过程)。
二、链式调用的实现
先看下实现源码:
由上面的实现,我们可以看到:
- then 方法中,创建并返回了新的 Promise 实例,这是串行Promise的基础,是实现真正链式调用的根本。
- then 方法传入的形参 onFulfilled 以及创建新 Promise 实例时传入的 resolve 放在一起,被push到当前 Promise 的 callbacks 队列中,这是衔接当前 Promise 和后邻 Promise 的关键所在。
- 根据规范,onFulfilled 是可以为空的,为空时不调用 onFulfilled。
看下动画演示:
(Promise 链式调用演示动画)
当第一个 Promise 成功时,resolve 方法将其状态置为 fulfilled ,并保存 resolve 带过来的value。然后取出 callbacks 中的对象,执行当前 Promise的 onFulfilled,返回值通过调用第二个 Promise 的 resolve 方法,传递给第二个 Promise。动画演示如下:
(Promise 链式调用 fulfilled)
为了真实的看到链式调用的过程,我写一个mockAjax函数,用来模拟异步请求:
除此之外,我给 Promise 的源码加上了日志输出并增加了构造顺序标识,可以清楚的看到构造以及执行过程:
执行结果如下:
通过打印出来的日志,可以看到:
-
构造 Promise-1 实例,立即执行 mackAjax('getUserId',callback);
-
调用 Promise-1 的 then 方法,注册 Promise-1 的 onFulfilled 函数。
-
then 函数内部构造了一个新的 Promise实例:Promise-2。立即执行 Promise-1 的 _handle方法。
-
此时 Promise-1 还是pending的状态。
-
Promise-1._handle 中就把注册在 Promise-1 的 onFulfilled 和 Promise-2 的 resolve 保存在 Promise-1 内部的 callbacks。
-
至此当前线程执行结束。返回的是 Promise-2 的 Promise实例。
-
1s后,异步请求返回,要改变 Promise-1 的状态和结果,执行 resolve(result)。
-
Promise-1 的值被改变,内容为异步请求返回的结果:"getUserId异步请求耗时1s"。
-
Promise-1 的状态变成 fulfilled。
-
Promise-1 的 onFulfilled 被执行,打印出了"getUserId异步请求耗时1秒"。
-
然后再调用 Promise-2.resolve。
-
改变 Promise-2 的值和状态,因为 Promise-1 的 onFulfilled 没有返回值,所以 Promise-2的值为undefined。
上例中,如果把异步的请求改成同步会是什么的效果?
感兴趣的可以自己去分析一下。
三、链式调用真正的意义
执行当前 Promise 的 onFulfilled 时,返回值通过调用第二个 Promise 的 resolve 方法,传递给第二个 Promise,作为第二个 Promise 的值。于是我们考虑如下Demo:
我们加了一层 then,来看下执行的结果:
链式调用可以无限的写下去,上一级 onFulfilled return 的值,会变成下一级 onFulfilled 的结果。可以参考Demo3:
我们很容易发现,上述 Demo3 中只有第一个是异步请求,后面都是同步的,我们完全没有必要这么链式的实现。如下一样能得到我们想要的三个结果: 分别打印出来的值。
那链式调用真正的意义在哪里呢?
刚才演示的都是 onFulfilled 返回值是 value 的情况,如果是一个 Promise 呢?是不是就可以通过 onFulfilled,由使用 Promise 的开发者决定后续 Promise 的状态。
于是在 _resolve 中增加对前一个 Promise onFulfilled 返回值的判断:
从代码上看,它是对 resolve 中的值作了一个特殊的判断,判断 resolve 的值是否为 Promise实例,如果是 Promise 实例,那么就把当前 Promise 实例的状态改变接口重新注册到 resolve 的值对应的 Promise 的 onFulfilled 中,也就是说当前 Promise 实例的状态要依赖 resolve 的值的 Promise 实例的状态。
执行的结果如下:
一样的,我做了一个演示动画,还原了这个过程:
(Promise 真正的链式调用)
至此,就实现了 Promise 链式调用的全部内容。链式调用是 Promise 难点,更是重点。一定要通过实例还有动画,深刻体会。下一节介绍 Promise 其它原型方法的实现。
更多内容敬请关注 vivo 互联网技术 微信公众号
注:转载文章请先与微信号:Labs2020 联系。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 上周热点回顾(2.17-2.23)
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)