所以你可以看到,其实 resolve 即使调用了,但内部并不一定就会发生状态变化,只有当 resolve 传递的参数类型既不是 Promise 对象类型,也不是具有 then 方法的 thenable 对象时,状态才会发生变化
而当传递的参数是 Promise 或具有 then 方法的 thenable 对象时,差不多又是相当于递归回到第一步的等待 task 函数的处理了
想想为什么需要这种处理,或者说,为什么需要这么设计?
这是因为,存在这样一种场景:有多个异步任务,这些异步任务之间是同步关系,一个任务的执行依赖于上一个异步任务的执行结果,当这些异步任务通过 then 的链式调用组合起来时,then 方法产生的新的 Promise 的状态变更是依赖于回调函数的返回值。所以这个状态变更需要支持当值类型是 Promise 时的异步等待处理,这条异步任务链才能得到预期的执行效果
当你们去看规范,或看规范的中文版翻译,其实有关于这个的更详细处理说明,比如开头给的链接的那篇文章里有专门一个模块:Promise 的解决过程,也表示成 [[Resolve]](promise, x) 就是在讲这个
但我想用自己的理解来描述,这样比较容易理解,虽然我也只能描述个大概的工作,更细节、更全面的处理应该要跟着规范来,下面就看看代码:
/** * resolve 的状态变更处理 */ _handleResolve(value) { if (this._status === this.PENDING) { // 1. 如果 value 是 Promise,那么等待 Promise 状态结果出来后,再重新做状态变更处理 if (value instanceof Promise) { try { // 这里之所以不需要用 bind 来注意 this 问题是因为使用了箭头函数 // 这里也可以写成 value.then(this._handleResole.bind(this), this._handleReject.bind(this)) value.then(v => { this._handleResolve(v); }, err => { this._handleReject(err); }); } catch(e) { this._handleReject(e); } } else if (value && value.then instanceof Function) { // 2. 如果 value 是具有 then 方法的对象时,那么将这个 then 方法当做 task 处理函数,把状态变更的触发工作交由 then 来处理,注意 this 的处理 try { const then = value.then; then.call(value, this._handleResolve.bind(this), this._handleReject.bind(this)); } catch(e) { this._handleReject(e); } } else { // 3. 其他类型,状态变更、触发成功的回调 this._status = this.RESOLVED; this._value = value; setTimeout(() = { this._resolvedCallback.forEach(callback => { callback(); }); }); } } } /** * reject 的状态变更处理 */ _handleReject(value) { if (this._status === this.PENDING) { this._status = this.REJECTED; this._value = value; setTimeout(() => { this._rejectedCallback.forEach(callback => { callback(); }); }); } } 第四步:thenthen 方法负责的职能其实也很复杂,既要返回一个新的 Promise,这个新的 Promise 的状态和结果又要依赖于回调函数的返回值,而回调函数的执行又要看情况是缓存进回调函数队列里,还是直接取依赖的 Promise 的状态结果后,丢到微任务队列里去执行
虽然职能复杂是复杂了点,但其实,实现上,都是依赖于前面已经写好的构造函数和状态变更函数,所以只要前面几个步骤实现上没问题,then 方法也就不会有太大的问题,直接看代码:
/** * then 方法,接收两个可选参数,用于注册回调处理,所以类型也是函数,且有一个参数,接收 Promise 执行结果,同时可返回任意值,作为新 Promise 的执行结果 */ then(onResolved, onRejected) { // then 方法返回一个新的 Promise,新 Promise 的状态结果依赖于回调函数的返回值 return new Promise((resolve, reject) => { // 对回调函数进行一层封装,主要是因为回调函数的执行结果会影响到返回的新 Promise 的状态和结果 const _onResolved = () => { // 根据回调函数的返回值,决定如何处理状态变更 if (onResolved && onResolved instanceof Function) { try { const result = onResolved(this._value); resolve(result); } catch(e) { reject(e); } } else { // 如果传入非函数类型,则将上个Promise结果传递给下个处理 resolve(this._value); } }; const _onRejected = () => { if (onRejected && onRejected instanceof Function) { try { const result = onRejected(this._value); resolve(result); } catch(e) { reject(e); } } else { reject(this._value); } }; // 如果当前 Promise 状态还没变更,则将回调函数放入队列里等待执行 // 否则直接创建微任务来处理这些回调函数 if (this._status === this.PENDING) { this._resolvedCallback.push(_onResolved); this._rejectedCallback.push(_onRejected); } else if (this._status === this.RESOLVED) { setTimeout(_onResolved); } else if (this._status === this.REJECTED) { setTimeout(_onRejected); } }); } 其他方面因为目的在于理清 Promise 的主要功能职责,所以我的实现版并没有按照规范一步步来,细节上,或者某些特殊场景的处理,可能欠缺考虑
比如对各个函数参数类型的校验处理,因为 Promise 的参数基本都是函数类型,但即使传其他类型,也仍旧不影响 Promise 的使用
比如为了避免被更改实现,一些内部变量可以改用 Symbol 实现