写点什么

【Promise 源码学习】第七篇 - 实现 Promise 返回值 x 的处理

作者:Brave
  • 2021 年 11 月 14 日
  • 本文字数:5143 字

    阅读完需:约 17 分钟

一,前言


上篇,实现了 Promise 的链式调用功能,主要涉及到以下几个点:


  • 介绍了 Promise 的链式调用,返回普通值和抛出异常的共 5 种情况;

  • 分析了当前 Promise 源码的问题以及解决方案;

  • Promise 链式调用的实现、功能测试、执行过程分析;


Promise A+ 规范中提到了对 then 中方法返回值 x 的处理,核心方法:resolvePromise


本篇,将实现 Promise 对返回值 x 的处理;


二,Promise A+ 规范


上一篇,在实现了 Promsie 链式调用的过程中,介绍了返回值 x 为普通值和抛出异常的情况;

返回值 x 还有可能是 Promise 类型,根据 Promise 状态决定执行成功/失败处理;


下面是 Promise A+ 规范中,与 “Promise 解析过程”有关的内容;

1,规范


Promise A+ 规范相关内容:


2,翻译


  • Promise 的解析过程,是以一个 promise 和 一个值 做为参数的抽象过程,可以表示为[[Resolve]](promise, x).如果 x 是一个 thenable,它试图使 promise 采用 x 状态,假设 x 的行为至少有点像 promise.否则,使用 x 的值执行 promise.

  • 这种 thenable 的特性使得 Promise 的实现更具有通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法即可;这同时也使遵循 Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。

  • 运行 [[Resolve]](promise, x) 需遵循以下步骤:

  • 2.3.1 如果 promise 和 x 指向同一个对象,使用 TypeError 作为据因拒绝执行 promise

  • 2.3.2 如果 x 为 Promise ,则使 promise 接受 x 的状态:

  • 2.3.2.1 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝;

  • 2.3.2.2 如果 x 处于执行态,用相同的值执行 promise;

  • 2.3.2.3 如果 x 处于拒绝态,用相同的据因拒绝 promise;

  • 2.3.3 如果 x 为对象或者函数:

  • 2.3.3.1 把 x.then 赋值给 then

  • 2.3.3.2 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise

  • 2.3.3.3 如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,第一个参数叫做 resolvePromise,第二个参数叫做 rejectPromise:

  • 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)

  • 2.3.3.3.2 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise

  • 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用

  • 2.3.3.3.4 如果调用 then 方法抛出了异常 e:

  • 2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之

  • 2.3.3.3.4.2 否则以 e 为据因拒绝 promise

  • 2.3.3.4 如果 then 不是函数,以 x 为参数执行 promise

  • 2.3.3 如果 x 不为对象或者函数,以 x 为参数执行 promise

  • 如果一个 promise 被一个循环的 thenable 链中的对象解决,而 [[Resolve]](promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励施者检测这样的递归是否存在,若检测到存在则以一个可识别的 TypeError 为据因来拒绝 promise。


三,resolvePromise 方法

resolvePromise 介绍


如果 then 中方法的返回值 x 为 promsie 类型;

那么将根据返回值 x 决定 promise2 执行成功/失败处理;


根据 promise A+ 规范,创建 resolvePromise 方法,统一处理 Promise 的解析流程;

2,创建 resolvePromise


根据 then 中方法的返回值 x 决定 promise2 走成功/失败(调用 resolve 或 reject);

所以,resolvePromise方法中入参需要promise2,返回值x ,promise2 的resolvereject

function resolvePromise(promise2, x, resolve, reject) {  // 根据返回值 x 的情况,调用 promise2 的 resolve 或 reject 函数}
复制代码

3,使用 resolvePromise


在上一篇中,当 then 中方法返回值 x 为普通值时,调用成功处理 resolve(x);

resolvePromise方法要统一处理返回值 x 的各种情况,所以先使用resolvePromise替换掉resolve(x)

/** * 根据返回值 x 情况,调用 promise2 的 resolve 或 reject */function resolvePromise(promise2, x, resolve, reject) {  resolve(x);// 返回值 x 为普通值的情况}
class Promise{ constructor(executor){...} then(onFulfilled, onRejected){ let promise2 = new Promise((resolve, reject)=>{ if(this.state === DULFILLED){ try{ let x = onFulfilled(this.value) // resolve(x) 由 resolvePromise 进行统一处理 resolvePromise(promise2, x, resolve, reject); }catch(e){ reject(e) } } if(this.state === REJECTED){ try{ let x = onRejected(this.reason) // resolve(x) 由 resolvePromise 进行统一处理 resolvePromise(promise2, x, resolve, reject); }catch(e){ reject(e) } } if(this.state === PENDING){ this.onResolvedCallbacks.push(()=>{ try{ let x = onFulfilled(this.value) // resolve(x) 由 resolvePromise 进行统一处理 resolvePromise(promise2, x, resolve, reject); }catch(e){ reject(e) } }) this.onRejectedCallbacks.push(()=>{ try{ let x = onRejected(this.reason) // resolve(x) 由 resolvePromise 进行统一处理 resolvePromise(promise2, x, resolve, reject); }catch(e){ reject(e) } }) } });
return promise2; }}
module.exports = Promise;
复制代码

4,promise2 的初始化问题

1,问题分析


代码修改之后,运行将会报错:

Cannot access 'promise2' before initialization
复制代码


  • 报错:“不能访问 promise2 在初始化之前”;

  • 原因:在 new promise 时,promise2 还没有完成初始化,所以 resolvePromise 中不能访问到 promise2;

2,参考规范


在 Promise A+ 规范中,可以找到此问题的说明:



  • 在当前的执行上下文栈中,onFulfilled 或 onRejected 是不能被直接调用的;

  • onFulfilled 或 onRejected 得是在当前事件循环后异步执行的;可以使用 setTimeoutsetImmediateMutationObsereverprocess.nextTick在 then 方法被调用后将创建一个新的栈;

3,问题解决


以 setTimeout 为例,使用 setTimeout 创建宏任务:

  then(onFulfilled, onRejected){    let promise2 = new Promise((resolve, reject)=>{      if(this.state === DULFILLED){        setTimeout(() => {  // 创建宏任务          try{            let x = onFulfilled(this.value)            resolvePromise(promise2, x, resolve, reject);          }catch(e){            reject(e)          }        }, 0);      }      if(this.state === REJECTED){        setTimeout(() => {  // 创建宏任务          try{            let x = onRejected(this.reason)            resolvePromise(promise2, x, resolve, reject);          }catch(e){            reject(e)          }        }, 0);      }      if(this.state === PENDING){        this.onResolvedCallbacks.push(()=>{           setTimeout(() => {  // 创建宏任务            try{              let x = onFulfilled(this.value)              resolvePromise(promise2, x, resolve, reject);            }catch(e){              reject(e)            }          }, 0);        })        this.onRejectedCallbacks.push(()=>{           setTimeout(() => {  // 创建宏任务            try{              let x = onRejected(this.reason)              resolvePromise(promise2, x, resolve, reject);            }catch(e){              reject(e)            }          }, 0);        })      }    });
return promise2; }
复制代码


备注:

  • try...catch...只能捕捉在同步代码中发生的异常,所以需要放在 setTimeout 内部;


原理:

  • 通过 setTimeout 创建了宏任务,会在下一个事件循环中被执行;

  • 保证调用 resolvePromise 时,promise2 实例已经创建完成;


四,实现 resolvePromise

promise2 === x 的情况

1,参考规范


  • 如果返回值 x 是 thenable 对象,就调用 then 方法,使用当前 promise 状态作为结果;

  • 如果 promise 和 x 是相同对象,使用 TypeError 作为原因,拒绝这个 promise;

2,测试原生 Promise

let promise2 = new Promise((resolve, reject) => {  resolve(1);}).then(() => {  return promise2; promise2 即为 then 中方法的返回值 x})
promise2.then(data => { console.log(data);}, err => { console.log(err);})
// 报错:[TypeError: Chaining cycle detected for promise #<Promise>]
复制代码


现象

  • 会抛出 TypeError 类型错误,promise 死循环;


原因

  • then 中方法返回 promise2,promise2 不会再次调用 resolve 或 reject;

3,功能实现


根据 Promise A+ 规范,代码实现:

function resolvePromise(promise2, x, resolve, reject){  // promise2 === x,promise 为拒绝状态 TypeError 作为拒因  if(promise2 === x){// 相同对象:判断引用地址相同    return reject(new TypeError('发生错误'))  }}
复制代码

4,测试手写 Promise


TypeError: 发生错误,与原生 Promise 表现一致;


2,x 为对象或函数的情况

1,参考规范



  • 如果 x 是一个 promise,就采用它的状态:

  • 如果 x 是一个对象或函数(有可能是 Promise);

2,代码实现:区分对象或函数/普通值


函数是 JS 的基本类型,可以通过typeof直接判断:

function resolvePromise(promise2, x, resolve, reject) {  // ...  // 返回值 x 是对象或函数的情况  if((typeof x === 'object' && x !== null) || typeof x === 'function'){    }else{// 返回值 x 是普通值的情况    resolve(x)  // 普通值直接调用 resolve(x)  }}
复制代码

3,x.then 异常的情况


备注: 当返回值 x 是对象或函数时,不一定是 Promise;比如:返回{}空对象;

1,参考规范



  • let then = x.then;

  • x.then 时可能发生异常,用此异常作为 promise 拒因;

2,代码实现:处理 x.then 异常


function resolvePromise(promise2, x, resolve, reject) {  // ...  if ((typeof x === 'object' && x !== null) || typeof x === 'function') {    // 若取 x.then 时发生异常,使用 e 作为拒因    try {      let then = x.then;    } catch (e) {      reject(e);    }  } else {    resolve(x)  }}
复制代码

4,then 是否为函数的情况

1,参考规范



  • 如果 then 是一个函数,调用它并让 x 作为它的 this;

2,代码实现:处理 then 是否为函数的 2 种情况


function resolvePromise(promise2, x, resolve, reject) {  // ...  if ((typeof x === 'object' && x !== null) || typeof x === 'function') {    try {      let then = x.then;      if(typeof then === 'function'){  // then 是函数:就认为 x 为 Promise 类型      }else{        resolve(x) // 按普通值处理      }    } catch (e) {      reject(e);    }  } else {    resolve(x)  }}
复制代码

5,x 为 promise 的情况


终点:当 x.then 为 function 时,就认为 x 是 Promise 了;

接下来,就调用 x.then,由它的结果决定执行成功或失败处理;

1,参考规范



  • 调用 then,并用 x 作为它的 this

  • 第一个参数是 resolvePromise,第二个参数是 rejectPromise

  • resolvePromise 的值叫做 y;rejectPromise 的值叫做 r;

2,代码实现:调用 then,决定 promise2 成功/失败


function resolvePromise(promise2, x, resolve, reject) {  if (promise2 === x) {    return reject(new TypeError('发生错误'))  }  if ((typeof x === 'object' && x !== null) || typeof x === 'function') {    try {      let then = x.then;      if(typeof then === 'function'){        // 返回值 x 为 Promise,调用 then 看结果        then.call(x, y => {          resolve(y)    // 成功        }, r => {          reject(r)     // 失败        });      }else{        resolve(x)      }    } catch (e) {      reject(e);    }  } else {    resolve(x)  }}
复制代码


判断是否为 Promise,如果是就它的执行 then 方法,决定 promise2 成功/失败;


五,结尾


本篇,主要实现了 Promise 对返回值 x 的处理,主要涉及到以下几个点:


  • 回顾 Promise A+ 规范;

  • 根据规范实现 resolvePromise 方法;


下一篇,完善 Promise 通过 promise-aplus-tests 测试;

用户头像

Brave

关注

还未添加个人签名 2018.12.13 加入

还未添加个人简介

评论

发布
暂无评论
【Promise 源码学习】第七篇 - 实现 Promise 返回值 x 的处理