写点什么

【Promise 源码学习】第六篇 - 实现 Promise 的链式调用

作者:Brave
  • 2021 年 11 月 13 日
  • 本文字数:6403 字

    阅读完需:约 21 分钟

一,前言


上一篇,实现了 Promise 对异步操作的支持,主要涉及以下几个点:


  • 测试 Promise 对异步操作的支持;

  • 分析当前 Promise 代码问题及解决方案;

  • 使用发布订阅思想实现对异步操作的支持;

  • Promise 异步操作的测试;


本篇,继续将实现 Promise 的链式调用;


二,前文回顾


在前几篇中,简单介绍了 Promise 的使用;翻译 Promise A+ 规范;实现简版 Promise;

上一篇,利用发布订阅模式,实现了 Promise 对异步操作的支持,主要就是以下两步:

  • 先将 then 方法中的成功/失败回调收集起来;

  • 当 promise 状态改变时(调用 resolve 或 reject),再依次执行对应的回调处理;


三,提出问题


omise 的一个核心特性,就是支持链式调用;

也因此,使用 Promise 能够很好的解决异步逻辑的“回调地狱”问题;


备注:

  • 关于 then 函数的返回值(也就是 Promise A+ 规范中提到的 x)存在多种返回值类型需要进行处理,将在下一篇展开详细的介绍;

  • 本篇主要以实现 Promise 链式调用功能为目的;暂不考虑返回值为 promise 的情况;

1,测试原生 Promise


  • 所谓的“普通值”,需要满足以下几点:

  • 不能是 Promise 对象;

  • 不能是报错或抛出异常,必须是能够正常返回的;比如 throw new Error() 就不属于普通值;

  • 成功或失败的回调处理没有返回值,相当于返回 undefined,属于普通值;

情况 1,成功回调中,返回普通值


let promise = new Promise((resolve, reject) => {  setTimeout(() => {       // 异步操作    resolve('执行成功');    // 进入成功回调处理  }, 1000)})
promise.then((value) => { console.log('[then1-onFulfilled]', value) return "then1-onFulfilled:" + value // return new Error() // 返回普通值 // return undefined // 返回普通值}, (reson) => { console.log('[then1-onRejected]', reson)}).then((value) => { console.log('[then2-onFulfilled]', value)}, (reson) => { console.log('[then2-onRejected]', reson)})
// [then1-onFulfilled] 执行成功// [then2-onFulfilled] then1-onFulfilled:执行成功
复制代码


  • 现象:

  • 1 秒后,执行了 then1 中的 onFulfilled 回调,继续 return value;

  • 然后,进入 then2 中的 onFulfilled 回调,可以获取到 then1 传递的 value;

  • 结论:

  • 成功回调中返回普通值,会进入下一个 then 的成功回调;

情况 2,失败回调中,返回普通值


let promise = new Promise((resolve, reject) => {  setTimeout(() => {    reject("执行失败") // 进入失败回调处理  }, 1000)})
promise.then((value) => { console.log('[then1-onFulfilled]', value) return "then1-onFulfilled:" + value}, (reson) => { console.log('[then1-onRejected]', reson) return "then1-onRejected:" + reson // 返回普通值 // return new Error() // 返回普通值 // return undefined // 返回普通值}).then((value) => { console.log('[then2-onFulfilled]', value)}, (reson) => { console.log('[then2-onRejected]', reson)})
// [then1-onRejected] 执行失败// [then2-onFulfilled] then1-onRejected:执行失败
复制代码


  • 现象:

  • 1 秒后,执行了 then1 中的 onRejected 回调,继续 return reson;

  • 然后,进入 then2 中的 onFulfilled 回调,可以获取到 then1 传递的 value;(这个 value 就是 then1 中 return 的普通值)

  • 结论:

  • 失败回调中返回普通值,会进入下一个 then 的成功回调;

  • 备注:

  • 由于每个 promise 实例的状态只能被修改一次,而示例中失败态的 promise 在第二次 then 时进入了成功的回调,说明 then1 处理完成后,返回了一个全新的 promise 实例;

情况 3,成功回调中,抛出异常


let promise = new Promise((resolve, reject) => {  setTimeout(() => {    resolve("执行成功")  }, 1000)})
promise.then((value) => { console.log('[then1-onFulfilled]', value) throw new Error("抛出异常")}, (reson) => { console.log('[then1-onRejected]', reson) return "then1-onRejected:" + reson}).then((value) => { console.log('[then2-onFulfilled]', value)}, (reson) => { console.log('[then2-onRejected]', reson)})
// [then1-onFulfilled] 执行成功// [then2-onRejected] Error: 抛出异常
复制代码


  • 现象:

  • 1 秒后,执行了 then1 中的 onFulfilled 回调,抛出异常;

  • 然后,进入 then2 中的 onRejected 回调,并捕获到 then1 抛出的异常;

  • 结论:

  • 成功回调中抛出异常,会进入下一个 then 的失败回调;

情况 4,失败回调中,抛出异常


let promise = new Promise((resolve, reject) => {  setTimeout(() => {    reject("执行失败")  }, 1000)})
promise.then((value) => { console.log('[then1-onFulfilled]', value)}, (reson) => { console.log('[then1-onRejected]', reson) throw new Error("抛出异常")}).then((value) => { console.log('[then2-onFulfilled]', value)}, (reson) => { console.log('[then2-onRejected]', reson)})
// [then1-onRejected] 执行失败// [then2-onRejected] Error: 抛出异常
复制代码


  • 现象:

  • 1 秒后,执行了 then1 中的 onRejected 回调,抛出异常;

  • 然后,进入 then2 中的 onRejected 回调,并捕获到 then1 抛出的异常;

  • 结论:

  • 失败回调中抛出异常,会进入下一个 then 的失败回调;

情况 5,executor 执行器中报错


如果在 executor 中就抛出了异常,还没有执行 resolve 和 reject:

备注:同步逻辑报错,非异步操作;

let promise = new Promise((resolve, reject) => {  throw new Error("executor 同步报错") // 抛出异常  resolve("处理成功")  // 不会被执行})promise.then((value) => {  console.log('[then1-onFulfilled]', value)}, (reson) => {  console.log('[then1-onRejected]', reson)}).then((value) => {  console.log('[then2-onFulfilled]', value)}, (reson) => {  console.log('[then2-onRejected]', reson)})// [then1-onRejected] Error: executor 同步报错// [then2-onFulfilled] undefined
复制代码


  • 现象:

  • executor 执行器函数中的同步操作抛出异常,进入 then1 的失败回调;

  • 由于 then1 中没有 return,相当于返回了 undefined 普通值,进入 then2 的成功处理;

  • 结论:

  • executor 同步逻辑抛出异常,进入 then 失败处理;

总结


根据以上 5 种情况进行总结:

  • Promise 的链式调用,当调用 then 方法后,将会返回一个全新的 promise 实例;

  • then 中方法(无论成功还是失败)返回普通值,都会做为下一次 then 的成功结果;

  • 执行器函数 和 then 中方法(无论成功还是失败)抛出异常,都会进入下一次 then 的失败结果;


备注:

  • 若 then 中方法返回一个 promise 实例,那么,将根据这个 promise 的状态决定执行成功还是失败的回调:

  • 成功的 promise:将返回值传递给下一个 then 的成功回调;

  • 失败的 promise:将失败原因传递给下一个 then 的失败回调;

2,测试手写 Promise


链式调用示例(与测试原生 Promise 代码相同):

let promise = new Promise((resolve, reject) => {  setTimeout(() => {    resolve('执行成功');  }, 1000)})
promise.then((value) => { console.log('[then1-onFulfilled]', value) return "then1-onFulfilled:" + value}, (reson) => { console.log('[then1-onRejected]', reson)}).then((value) => { console.log('[then2-onFulfilled]', value)}, (reson) => { console.log('[then2-onRejected]', reson)})
// TypeError: Cannot read property 'then' of undefined
复制代码


现象

  • 会在第二个 then 方法调用处抛出异常;

3,问题分析


  • 现象:手写的 Promise 源码,目前只支持单次调用 then 方法,多次调用就会报错;

  • 原因:当前 Promise 在调用 then 方法后,没有返回新的 Primise 对象,所以不能继续 .then

class Promise{  constructor(executor){    this.state = PENDING;    this.value = undefined;    this.reason = undefined;    this.onResolvedCallbacks = [];    this.onRejectedCallbacks = [];    const reslove = (value) =>{...}    const reject = (reason) =>{...}    executor(reslove, reject);  }    then(onFulfilled, onRejected){    if(this.state === PENDING){      this.onResolvedCallbacks.push(()=>{        onFulfilled(this.value) // 被调用后,没有返回新的 promise      })      this.onRejectedCallbacks.push(()=>{        onRejected(this.value) // 被调用后,没有返回新的 promise      })    }    if(this.state === DULFILLED){      onFulfilled(this.value) // 被调用后,没有返回新的 promise    }     if(this.state === REJECTED){      onRejected(this.reason) // 被调用后,没有返回新的 promise    }        // then 处理完成后,并没有继续返回 promise 对象,所以不能继续 .then  }}
module.exports = Promise;
复制代码

4,解决方案


在 then 方法的回调函数执行完成后,需要获取到函数的返回值,并包装成为全新的 promise 实例;

  • 获取 then 回调处理的返回值;(这个返回值,就是 Promise A+ 规范中的 x)

  • 包装成为全新的 promise 实例;

  • 异常处理:executor 和 then 方法中 添加 try..catch... 进行异常捕获;


四,Promise 链式调用的实现

获取 then 回调处理返回值

class Promise{  constructor(executor){...}  then(onFulfilled, onRejected){      if(this.state === DULFILLED){        // 拿到回调处理的返回值        let x = onFulfilled(this.value)      }      if(this.state === REJECTED){        let x = onRejected(this.reason)        resolve(x)      }      if(this.state === PENDING){        this.onResolvedCallbacks.push(()=>{           let x = onFulfilled(this.value)           resolve(x)        })        this.onRejectedCallbacks.push(()=>{           let x = onRejected(this.reason)          resolve(x)        })      }          // 包装回调处理的返回值 x ,成为一个全新的 Promise 实例    let promise2 = new Promise((resolve, reject)=>{        // 问题:拿不到 x,不在一个作用域中    });
// 返回新的 promise 实例,这样外部可以继续调用 .then return promise2; }}
复制代码


问题

  • then 函数返回新创建的 promise2,但 promise2 内部是拿不到返回值 x 的;

2,then 返回全新 promise


要解决上边的问题,就需要在 promise2 的 executor 执行器中拿到返回值 x;

class Promise{  constructor(executor){...}  then(onFulfilled, onRejected){    // 获取成功/失败回调的返回值,并包装成为新的 Promise 实例    let promise2 = new Promise((resolve, reject)=>{      // 同步      if(this.state === DULFILLED){        // 拿到回调处理的返回值        let x = onFulfilled(this.value)        // 更改 promise2 的状态,并返回这个新的 promise 实例(即 primise2)        resolve(x)      }      // 同步      if(this.state === REJECTED){        let x = onRejected(this.reason)        resolve(x)      }      // 异步      if(this.state === PENDING){        this.onResolvedCallbacks.push(()=>{           let x = onFulfilled(this.value)           resolve(x)        })        this.onRejectedCallbacks.push(()=>{           let x = onRejected(this.reason)          resolve(x)        })      }    });
// 返回新的 promise 实例 return promise2; }}
复制代码

3,对异常的处理


executor 执行器函数和 then 中方法(无论成功还是失败)抛出异常,都会进入下一次 then 的失败结果;

只需在以下几个位置捕获异常,并执行 reject 即可:

  • executor 执行器函数(在简版 Promsie 中已实现)

  • then 方法中的同步操作(DULFILLED 和 REJECTED),回调函数执行时;

  • then 方法中的异步操作(PENDING),回调函数执行时;

class Promise{  constructor(executor){    const reslove = (value) =>{...}    const reject = (reason) =>{...}    try{      executor(reslove, reject);     }catch(e){      reject(e)    // 捕获异常    }  }
then(onFulfilled, onRejected){ let promise2 = new Promise((resolve, reject)=>{ if(this.state === DULFILLED){ try{ let x = onFulfilled(this.value) resolve(x) }catch(e){ reject(e) // 捕获异常 } } if(this.state === REJECTED){ try{ let x = onRejected(this.reason) resolve(x) }catch(e){ reject(e) // 捕获异常 } } if(this.state === PENDING){ this.onResolvedCallbacks.push(()=>{ try{ let x = onFulfilled(this.value) resolve(x) }catch(e){ reject(e) // 捕获异常 } }) this.onRejectedCallbacks.push(()=>{ try{ let x = onRejected(this.reason) resolve(x) }catch(e){ reject(e) // 捕获异常 } }) } });
return promise2; }}
复制代码

4,测试手写 Promsie 的链式调用


let promise = new Promise((resolve, reject) => {  setTimeout(() => {       // 异步操作    resolve('执行成功');    // 成功态,传参  }, 1000)})
promise.then((value) => { console.log('[then1-onFulfilled]', value) return "then1-onFulfilled:" + value}, (reson) => { console.log('[then1-onRejected]', reson)}).then((value) => { console.log('[then2-onFulfilled]', value)}, (reson) => { console.log('[then2-onRejected]', reson)})
// [then1-onFulfilled] 执行成功// [then2-onFulfilled] then1-onFulfilled:执行成功
复制代码


略:5 种情况与原生 Promise 表现一致;


五,执行过程分析


分析当前 Promise 的执行过程:

  • 首先,应用层 new Promise 创建 promise 实例时,传入的 executor 执行器会在 constructor 中被立即执行;

  • 应用层调用 promise.then 时,传入成功/失败状态的处理逻辑;

  • 进入 then 处理,在源码中,会先创建一个 promise2 实例;那么,promise2 的 executor 执行器也会被立即执行;

  • 此时,原本 promise 对三态的处理,就被包裹在了 promise2 的 executor 中;因此,从执行上不会受到影响,同样会被立即执行;

  • 这样,在包装 then 返回值 x 的 promise2 中,就可以拿到这个返回值 x 了!

  • 当 promise2 调用了 resolve(x),就会进入外部 promise.then 的成功回调处理,并返回一个普通值;

  • 源码中的 promise2 中就拿到了 promise.then 的返回值 x,并通过 reslove(x) 将 promise2 的状态更新为成功态;

  • 然后,就进入了应用层 Promise 链式调用第二个 then 的成功回调处理。。。。


备注:

  • 存在递归操作:then 方法中的回调处理将返回一个 promise,promise 继续 then,继续返回 promise。。。



六,结尾


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


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

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

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


备注:


  • 由于当前并未考虑到返回值 x 为 promise 的情况,所以代码并非最终实现;

  • 下一篇集中处理返回值 x,即实现 Promise 源码中的核心方法:resolvePromise


下一篇,实现 Promise 返回值 x 的处理;

用户头像

Brave

关注

还未添加个人签名 2018.12.13 加入

还未添加个人简介

评论

发布
暂无评论
【Promise 源码学习】第六篇 - 实现 Promise 的链式调用