「重温JS」promise实现过程

356 阅读20分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情

Promise简介

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise的特点

  1. 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有在Promsie内部调用resolve 或 reject,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。

Promise的缺点

  1. Promise对象一旦新建就会立即执行,无法中途取消
  2. 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  3. 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

Promise的状态

使用new Promise创建的promise对象有三个状态

  • Fulfilled
    • 成功状态,此时会调用 onFulfilled回调
  • Rejected
    • 失败状态, 此时会调用 onRejected 回调
  • pending
    • Promise对象被创建后的初始状态

\

基本用法

Promise对象是一个类,用来生成Promise实例。

下面代码创造了一个Promise实例。

const promise = new Promise((resolve, reject) => {
  //一些异步操作代码
  
  if('异步操作成功'){
      resolve(value)
  }else{
      reject(err)
  }
})

Promise类接受一个函数作为参数,这个函数我们称之为执行器,在传入之后会立即执行。该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;

reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成以后,如何Promise对象传出的值?

一、 实例.then() 中传入两个回调函数

promise.then(val => {
    //成功回调
}, err => {
    //失败回调
})

then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。这两个函数都是可选的,不一定要提供。它们都接受Promise对象传出的值作为参数。

二、实例.then().catch()

promise.then(res => {
    //成功回调
  }
).catch(err => {
  //失败回调
})

then表示成功回调, catch表示失败回调

手写Promise

下面我们通过手写promise的方式来加深理解,如有错误的地方,烦请指正,一起进步

核心逻辑实现

  1. Promise是一个类,在创建时会传入一个函数,这个函数会立即执行
//将Promise的三个状态定义为常量,修改时不必手敲单词,编辑器会有提示
const PENDING = 'pending',
      FULFILLED  = 'fulfilled',
      REJECTED = 'rejected'
class MyPromise{
  constructor(executor){
    //在constructor中立即执行传入的函数
      executor(this.resolve, this.reject)
  }
  //初始状态是等待
  status = PENDING
  //resolve和reject是在创建实例时直接调用的,要用箭头函数将this绑定给当前实例
  resolve = () => {
    
  }
  reject = () => {
    
  }
}
  1. Promise有三种状态, 分别为: 成功fulfilled, 失败rejected, 等待pending

pending -> fulfilled

pending -> rejected

状态只能有上面两种变化方式,且状态一旦更改便不可再次更改,resolve和reject是用来更改Promise状态的

因此,resolve和reject中分别这样写

class MyPromise{
  ...
  status = PENDING
  resolve= () => {
      if(this.status !== PENDING) return
      this.status == FULDILLED
  }
  reject = () => {
      if(this.status !== PENDING) return
      this.status == REJECTED
  }
  ...
}
  1. then 方法内部做的事情就是判断状态, 如果是成功就调用成功的回调函数, 失败就调用失败的回调函数,then方法时被定义在原型对象上的方法

then成功回调有一个参数表示成功的值(调用resolve时传入的值), 失败回调有一个参数表示失败的原因(调用reject时传入的值)

class MyPromise{
  ...
  status = PENDING
  value = undefined
  resaon = undefined
  resolve= (value) => {
      //判断状态
      if(this.status !== PENDING) return
      this.status == FULDILLED
      //保存成功的值
      this.value = value
  }
  reject = (reason) => {
      //判断状态
      if(this.status !== PENDING) return
      this.status == REJECTED
      //保存失败的值
      this.reason = reason
  }
  //在调用then方法时会传入成功和失败的回调
  then(successCallback, failCallback){
      //首先判断状态
    //然后我们发现then方法成功失败回调的参数此时没有,其实他们就是调用resolve和reject时传入的值
      if(this.status == FULFILLED){
        successCallback(this.value)
      }else if(this.status == REJECTED){
        failCallback(this.reason)
      }
  }
  ...
}

核心逻辑完整代码

const PENDING = 'pending',
      FULFILLED  = 'fulfilled',
      REJECTED = 'rejected'
class MyPromise{
  constructor(executor){
      executor(this.resolve, this.reject)
  }
  status = PENDING
  value = undefined
  reason = undefined
  resolve = (value) => {
    if(this.status !== PENDING) return
    this.status = FULFILLED
    this.value = value
  }
  reject = (reason) => {
    if(this.status !== PENDING) return
    this.stauts = REJECTED
    this.reason = reason
  }
  then(successCallback, failCallback){
    if(this.status == FULFILLED){
       successCallback(this.value)
     }else if(this.status == REJECTED){
        failCallback(this.reason)
    }
  }
}

处理异步逻辑

问题分析

使用自己写的promise模拟一下异步操作:

首次,导出我们写的MyPromise类

class MyPromise{
  .....
}
//使用node运行,所以用commonJS规范导出
module.exports = MyPromise

同一目录下,新建一个js文件 导入MyPromise类

const MyPromise = require('./myPromise')

let mp = new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve('成功')
    },2000)
})

mp.then(res => {
  console.log(res)
})

运行结果:

我们可以看到,什么都没有输出, 原因如下:

  • then方法是同步的,会自上而下立即执行
  • 而resolve在2S之后异步执行的,2S之后才会将promise状态改为成功
  • then方法中只判断了成功和失败,并没有判断等待的状态
  • 因此对于异步的resolve和reject,then方法中的状态依旧是等待,什么都不会执行

修改代码

1、在当前实例中添加两个属性,接收成功回调和失败回调

2、then方法中添加当前状态是等待时的处理,将传入的成功回调和失败回调分别赋值给实例的两个属性

3、当调用resolve时,执行当前实例的成功回调

调用reject时, 执行当前实例的失败回调

class MyPromise {
  ....
  resolve = (value) => {   //将状态改为成功
    if(this.status !== PENDING) return
    this.status = FULFILLED
    //保存成功之后的值
    this.value = value
    //调用当前实例保存的成功回调 并传入参数
    this.successCallback && this.successCallback(this.value)
  }
  reject = (reason) => {   //将状态改为失败
    if(this.status !== PENDING) return
    this.status = REJECTED
    // 保存失败的原因
    this.reason = reason
    //调用当前实例的失败回调 并传入参数
    this.failCallback && this.failCallback(this.reason)
  }
  //存储成功回调和失败回调
  successCallback = null
  failCallback = null
  then(successCallback, failCallback){
    if(this.status === FULFILLED){   //状态是成功
      successCallback(this.value)
    }else if(this.status === REJECTED){   //状态是失败
      failCallback(this.reason)
    }else{     //状态是等待时,将传进来的处理函数保存在当前实例内
         this.successCallback = successCallback
         this.failCallback = failCallback
    }
  }
  ....
}

再次运行下当前出现问题的代码:

可以看到,在2S后输出了成功,代码修改完成

\

then方法多次调用

问题分析

我们前面说过,Promise状态一旦改变,便不会再次改变。任何时候都可以得到这个结果

是不是意味着,我们可以多次通过.then的方式,传入不同的处理函数来处理Promise的结果

此时就会出现一个问题:

  • 如果是同步调用resolve或reject, 我们在调用then方法时,直接执行传入的处理函数即可,但是我们Promise一般都是来处理异步操作的
  • 处理异步操作时, 如果多次调用then方法,我们内部保存的成功和失败回调一定会被最后一次调用then时传入的回调函数覆盖, 也只会执行最后一次回调函数
const MyPromise = require('./myPromise')
new Promise((resolve, reject) => {
  resolve('成功')
})

let mp = new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve('成功')
    }, 2000)
})

mp.then(res => {
  console.log(res + '1')
})

mp.then(res => {
  console.log(res + '2')
})

mp.then(res => {
  console.log(res + '3')
})

输出:

\

修改代码

1、修改MyPromise类中保存成功失败回调函数属性的数据类型

之前是直接then中传递来的成功失败回调为它们赋值

现将它们改为数组, 每次调用then方法,就将回调函数添加到数组中

class MyPromise{
    ...
    successCallback = []
    failCallback = []
}

2、调用then方法时,将传递进来的回调函数push到MyPromise内部的回调数组中

class MyPromise{
    ...
    successCallback = []
    failCallback = []
    ...
    then(successCallback, failCallback){
        //判断状态
      if(this.status == FULFILLED){
        successCallback(this.value)
      }else if(this.status == REJECTED){
        failCallback(this.reason)
      }else{   //状态为等待,向MyPromise内部保存回调函数的数组中添加传入的回调函数
        this.successCallback.push(successCallback)
        this.failCallback.push(failCallback)
      }
    }
}

3、调用resolve或reject时

class MyPromise{
    ...
    status = PENDING
    value = undefined
    reason = undefined
    successCallback = []
    failCallback = []
    ...
    resolve = (value) => {
      //按照先进先出的顺序 执行successCallback中传入的成功回调
       while(this.successCallback.length){
           this.successCallback.pop()(value)
       }
    }
    reject = (reason) => {
      //按照先进先出的顺序,执行failCallback中传入的失败回调
      while(this.failCallback.length){
        this.failCallback.shift()(reason)
      }
    }
    then(successCallback, failCallback){
        //判断状态
      if(this.status == FULFILLED){
        successCallback(this.value)
      }else if(this.status == REJECTED){
        failCallback(this.reason)
      }else{   //状态为等待,向MyPromise内部保存回调函数的数组中添加传入的回调函数
        this.successCallback.push(successCallback)
        this.failCallback.push(failCallback)
      }
    }
}

再次执行上面多次调用then方法的代码

现在可以看到2s后一次执行了我们传入的处理函数

then方法链式调用

我们知道,promise的then方法支持链式调用, 后面一个then方法拿到的值 是 上一个then方法返回的值

例:

let mp = new MyPromise((resolve, reject) => {
  resolve(123)
})

mp.then(res => {
  console.log(res)
  return 234
}).then(res => {
  console.log(res)
})

要实现链式调用,需要做到以下两点:

  1. then方法的链式调用,我们知道,只有promise对象才有then方法, 因此每一个then方法都应该返回一个promise对象
  2. 将上一个then方法的返回值传递给下一个then方法

需求1,then方法的链式调用

class MyPromise{
  ...
  then(successCallback, failCallback) {
    //在then方法中创建一个promise对象并返回
    // 创建promise对象时,传入的函数,会立即执行
    // 而我们then方法中原来的代码也需要立即执行,因此直接将then中原有的代码,放入返回的promise对象传入的函数中
    const promise2 = new MyPromise((resolve, reject) => {
      //判断状态
      if(this.status == FULFILLED){
        successCallback(this.value)
      }else if(this.status == REJECTED){
        failCallback(this.reason)
      }else{   //状态为等待,处理异步逻辑
        this.successCallback.push(successCallback)
        this.failCallback.push(failCallback)
      }
    })
    return promise2
  }
  ...
}

需求2、将上一个then方法的返回值传递给下一个then方法

class MyPromise{
  ...
  then(successCallback, failCallback) {
    const promise2 = new MyPromise((resolve, reject) => { 
      if(this.status == FULFILLED){
        //用一个变量去接收上个then返回的值
        let x = successCallback(this.value)
        //直接调用返回的promise的resolve方法,
        resolve(x)
      }else if(this.status == REJECTED){
        failCallback(this.reason)
      }else{  
        this.successCallback.push(successCallback)
        this.failCallback.push(failCallback)
      }
    })
    return promise2
  }
  ...
}

此时看起来代码就差不多了,但是我们忽略了一个问题, 上一个then方法可以返回一个普通值,也可以返回一个promise对象

  • 当上一个then方法返回一个普通值时,我们直接返回一个promise对象是没问题的
  • 当上一个then方法返回一个promise对象时, 我们需要去判断状态返回的promise对象的状态,从而确定使用成功还是失败回调
class MyPromise{
  ...
  then(successCallback, failCallback) {
    const promise2 = new MyPromise((resolve, reject) => { 
      if(this.status == FULFILLED){
        //用一个变量去接收上个then返回的值
        //判断x的类型是普通值还是promise对象
        //如果是普通值,直接调用返回的promise对象的resolve方法
        //如果是promise对象,确定promise对象返回的结果
        //在根据promise对象返回的结果,决定调用resolve还是reject
        let x = successCallback(this.value)
        resolvePromise(x, resolve, reject)
      }else if(this.status == REJECTED){
        failCallback(this.reason)
      }else{  
        this.successCallback.push(successCallback)
        this.failCallback.push(failCallback)
      }
    })
    return promise2
  }
  ...
}
  
//定义通用函数,处理上一个then返回值的类型,被确定下一个then中调用成功回调还是失败回调
function resolvePromise(x, resolve, reject){
  if(x instanceof MyPromise){  //x 是promise对象
     x.then(val => resolve(val), reason => reject(reason))
  }else{   //x是普通值
    resolve(x)
  }
}

测试代码:

let mp = new MyPromise((resolve, reject) => {
  resolve(123)
})

function other() {
  return new MyPromise((resolve, reject) => {
    resolve('other')
  })
}
mp.then(res => {
  console.log(res)
  return other()
}).then(res => {
  console.log(res)
})

看下结果:

完成!

then方法识别是否返回自身

\

我们在then方法的链式调用中,是不能返回自身的promise对象的

用JS给我们提供的Promise类模拟一下:

const p = new Promise((resolve, reject) => {
    resolve('Promise')
  })

  let p1 = p.then(res => {
    console.log(res)
    // 我们知道 p.then方法返回的一定是一个promise对象
    //用p1去接收返回的promise对象
    //而又将返回的promsie对象return了出去
    //此时就会发生promise对象的循环调用,控制台就会抛出错误
    return p1
  })

运行代码,可以看到控制台出现以下报错信息:

大意是:promise对象被循环调用了,这样是不可以的

如何在我们写的程序中判断返回的promise对象是不是自身?

回到我们的then方法中

class MyPromise{
  ...
  then(successCallback, failCallback) {
    //promise2是我们要返回的promise对象
    const promise2 = new MyPromise((resolve, reject) => { 
      if(this.status == FULFILLED){
        //只有当new MyPromise执行结束,才会存在promise2,因此将这段代码变为异步执行
        setTimeout(() => {
            //x是上一个.then返回的值
            //我们只要判断promise2 是否=== x 即可
            let x = successCallback(this.value)
            //将判断是否相等操作统一给到resolvePromise处理
            //将promise2传入resolvePromise中
            resolvePromise(promise2, x, resolve, reject)
        },0)
      }else if(this.status == REJECTED){
        failCallback(this.reason)
      }else{  
        this.successCallback.push(successCallback)
        this.failCallback.push(failCallback)
      }
    })
    return promise2
  }
  ...
}

  function resolvePromise(promise2, x, resolve, reject){
    //如果相等,抛出类型错误
    if(promise2 === x){
      return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if(x instanceof MyPromise){  //x 是promise对象
       x.then(val => resolve(val), reason => reject(reason))
    }else{   //x是普通值
      resolve(x)
    }
  }

使用我们写的MyPromise看下效果:

const promise = new MyPromise((resolve, reject) => {
  resolve('Promise')
})

let p1 = promise.then(res => {
  console.log(res)
  return p1
})

p1.then(
  res => {
    console.log(res)
  },
  err => {   //失败回调
    console.log(err)
  }
)

输出结果:

我们可以看到,由于返回了自身, 我们在p1的then方法的失败回调中得到了错误信息

运行时错误捕获及then方法链式调用完整化

在promise中,当执行器当中的代码在执行时发生错误 或 then方法回调函数中抛出错误时,都是可以捕获到的,从而在失败回调中拿到结果

\

例:

const promise = new Promise((resolve, reject) => {
  throw new Error('executor error')  //模拟promise等待状态中运行时发生错误
  resolve('promise')
}).then(res => {
  console.log(res)
}, err => {
  console.log('onRejected')
  console.log(err.message)
  throw new Error('then方法抛出错误') 
}).then(res => {
  console.log(res)
}, err => {
  console.log('onRejected1')
  console.log(err.message)
})

执行结果:

\

接下来修改代码,实现错误捕获:

1、 执行器中错误捕获

class MyPromise {
    constructor(executor){
        try {
          //尝试去执行 执行器
          executor(this.resolve, this.reject)
        }catch(err){
            //执行器执行过程中遇到错误对象,直接调用reject,并传入错误对象
            this.reject(err)
        }
    }
}

2、捕获then方法回调函数中抛出的错误

当then方法回调函数抛出错误时, 错误信息应该在下一个then方法失败回调中能接收到

class MyPromise {
  constructor(){
  
  }
  ...
  then(successCallback, failCallback) {
    //promise2是我们当前then执行之后要返回的promise对象
    const promise2 = new MyPromise((resolve, reject) => {
      if(this.status == FULFILLED){   //当前promise对象状态是成功的
        setTimeout(() => {
          try{     //没有遇到错误就正常运行
            let x = successCallback(this.value)
            resolvePromise(promise2,x, resolve, reject)
          }catch (err){
            //then方法回调函数中抛出错误,直接调用返回的promise的reject方法。以执行下一个promise的失败回调
            reject(err)
          }
        }, 0)
      }
      .....
    })
    return promise2
  }
}

}

到目前为止,都只处理了 当前promise对象是成功的情况, 当前promise是失败的情况和当前promise是等待的状况 还没有写, 下面补一下:

当前promise是失败

class MyPromise {
  constructor(){
  
  }
  ...
  then(successCallback, failCallback) {
    //promise2是我们当前then执行之后要返回的promise对象
    const promise2 = new MyPromise((resolve, reject) => {
      if(this.status == FULFILLED){   //当前promise对象状态是成功的
        ...
      }else if(this.status == REJECTED){    //当前promise状态是失败
          setTimeout(() => {
              try {
                let x = failCallback(this.reason)
                resolvePromise(promise2,x, resolve, reject)
              }catch(err) {
                reject(err)
              }
            }, 0)
      }else{
          ...
      }
      .....
    })
    return promise2
  }
}

}

当前promise是异步进行的

class MyPromise{
  constructor(executor){
    ...
  }
  ...
   then(successCallback, failCallback) {
    //promise2是我们要返回的promise对象
    const promise2 = new MyPromise((resolve, reject) => {
      //判断状态
      if(this.status == FULFILLED){
        ...
      }else if(this.status == REJECTED){
        ...
      }else{   //状态为等待,处理异步逻辑
        //向成功回调中push一个匿名函数
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value)
              resolvePromise(promise2, x, resolve, reject)
            }catch(err){
              reject(err)
            }
          }, 0)
        })
        //向失败回调中push 匿名函数
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            }catch (err) {
              reject(err)
            }
          },0)
        })
      }
    })
    return promise2
  }
}

当前promise异步执行到resolve或reject调用时:

 resolve = (value) => {   
    if(this.status !== PENDING) return
    this.status = FULFILLED
    //保存成功之后的值
    this.value = value
    while(this.successCallback.length){
      //不再需要参数了,因为resolve将value赋值给this.value, 而successCallback中的每一项需要this.value作为参数,直接用即可
      this.successCallback.shift()()
    }
  }
  reject = (reason) => {   
    if(this.status !== PENDING) return
    this.status = REJECTED
    // 保存失败的原因
    this.reason = reason
    while(this.failCallback.length){
      //同resolve 不再需要参数
      this.failCallback.shift()()
    }
  }

测试一下:

new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功')
  }, 2000)
}).then(res => {
  console.log(res)
  return new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve('链式调用成功')
    }, 1000)
  })
}).then(res => {
  console.log(res)
})

不出意外的话, 上面的代码会在2s后输出 成功, 1S后输出 链式调用成功

执行看下结果👇🏻

then方法回调函数变为可选参数

问题分析

1、 原生promise中

在原生的promise中,可以这样写:

const promise = new Promise((resolve, reject) => {
  resolve(123)
})

promise.then().then().then().then(res => console.log(res))

我们在进行then方法的链式调用时,可以不传递回调函数, promise的状态会一直向后传递给有回调函数的then方法,进而得到promise的执行结果👇🏻

\

2、我们写的MyPromise中进行同样的操作:

const promise = new MyPromise((resolve, reject) => {
  resolve(123)
})

promise.then().then().then().then(res => console.log(res))

可以看到,什么都没有输出:

说明我们的状态并没有传递给后面的then方法,先将then方法的调用方式改成这样:

const promise = new MyPromise((resolve, reject) => {
  resolve(123)
})

promise
  .then((res) => res)
  .then((res) => res)
  .then((res) => res)
  .then((res) => console.log(res));

再次执行

我们通过手动在then方法中,将得到的结果, 传递给下一个then的方式,实现了和原生一样的效果,接下来试着把这一操作放入 MyPromise类中

修改代码

class MyPromise{
  ...
  then(successCallback, failCallback){
    //判断是否传入成功失败回调,如果没传就为其赋默认值
    // 对于成功回调,直接将得到的结果返回
    // 对于失败回调,抛出错误原因
    successCallback = successCallback ? successCallback : value => value
    failCallback = failCallback ? failCallback : reason => {throw reason}
    ...
  }
}

如果调用then方法没有传入相应的成功失败回调函数, 我们就在MyPromise类中为其赋予默认值,将结果传递给下一个then方法

静态方法 resolve 和 reject的实现

在原生的promise中,我们可以直接调用Promise类的resolve和reject方法去生成promise对象

像这样👇🏻

原生Promise 静态方法生成状态是成功的promise对象

Promise.resolve(123)
  .then(res => {
    console.log(res)
  })
// 123

原生Promise 静态方法生产状态是失败的promise对象

Promise.reject(new Error('失败了'))
  .then(null, reason => console.log(reason.message))
// 失败了

下面用我们的MyPromise来模拟实现一下, 因为resolve和reject方法是有Promise类去调用的,所以他们一定都是静态方法, 且他们的调用结果都会返回promise

用MyPromise实现私有方法 resolve和reject

class MyPromise{
  ...
  static resolve(value){
    //如果value是promise对象,直接返回
    if(value instanceof MyPromise) return value
    //如果value是普通值,返回一个状态是成功的promise对象
    return new MyPromise(resolve => resolve(value))
  }
  static reject(reason){
    //这里一定是返回一个失败的promise对象
    return new MyPromise((resolve, reject) => reject(reason))
  }
}

测试一下代码:

//resolve普通值
MyPromise.resolve(123)
  .then(res => console.log(res))
//resolvepromise对象
let p = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(2000)
  }, 2000)
})
MyPromise.resolve(p)
  .then(res => {
    console.log(res)
  })
//reject
MyPromise.reject(new Error('MyPromise失败'))
  .then(null, reason => console.log(reason.message))

输出结果:

先后输出

123

MyPromise失败

2s以后输出

2000

完整代码

到这里,我们手写promise基本完工了,贴下完整代码,以及自己的总结

const PENDING = 'pending',
      FULFILLED = 'fiulfilled',
      REJECTED = 'rejected'
// promise是一个类,创建时会传入一个函数,这个函数会立即执行
class Promise2{
  constructor(executor){
    //当promise内部运行出错时,直接调用this.reject
    try{
      executor(this.resolve, this.reject)
    }catch(err) {
      this.reject(err)
    }
  }
  status = PENDING
  value = undefined
  reason = undefined
  //保存多次调用then方法传入的成功和失败回调函数
  successCallback = []
  failCallback = []
  resolve = (value) => {
    //改变promise状态为成功
    if(this.status !== PENDING) return
    this.status = FULFILLED
    //保存成功的结果
    this.value = value
    //先进先出的方式执行多次调用then方法传入的回调函数
    while(this.successCallback.length){
      this.successCallback.shift()()
    }
  }
  reject = (reason) => {
    //改变promise状态为失败
    if(this.status !== PENDING) return
    this.status = REJECTED
    //保存失败原因
    this.reason = reason
    while(this.failCallback.length){
      this.failCallback.shift()()
    }
  }
  //then方法接收成功和失败回调函数
  //then方法要做的第一件事是去判断当前promise是成功的还是失败的
  then(successCallback, failCallback){
    //将then方法中的成功失败回调变为可选参数
    successCallback = successCallback ? successCallback : val => val
    failCallback = failCallback ? failCallback : reason => {throw reason}
    //实现then方法的链式调用,首先then方法应该返回一个promise对象
    const p2 = new Promise2((resolve, reject) => {
      if(this.status == FULFILLED){  //调用then方法时当前状态是成功
        setTimeout(()=> {
          //错误捕获, 当运行出现错误时, 直接调用返回的promise对象的reject方法
          try{
            let x = successCallback(this.value)
            resolvePromise(p2, x, resolve, reject)
          }catch(err){
            reject(err)
          }
        }, 0)
      }else if(this.status == REJECTED){   //调用then方法时当前状态是失败
        setTimeout(() => {
          try{
            let x = failCallback(this.reason)
            resolvePromise(p2, x, resolve, reject)
          }catch(err){
            reject(err)
          }
        }, 0)
      }else{   //处理异步操作
        //此时并不知道当前promise是成功还是失败,将传入的处理函数都保存起来,等到调用resolve或reject是再去决定使用哪个回调
        this.successCallback.push(() => {
          setTimeout(()=> {
            try{
              let x = successCallback(this.value)
              resolvePromise(p2, x, resolve, reject)
            }catch(err){
              reject(err)
            }
          }, 0)
        })
        this.failCallback.push(()=> {
          setTimeout(() => {
            try{
              let x = failCallback(this.reason)
              resolvePromise(p2, x, resolve, reject)
            }catch(err){
              reject(err)
            }
          }, 0)
        })
      }
    })
    return p2
  }
}

function resolvePromise(p2, x, resolve, reject){
  //如果返回的promise对象与和上一个.then执行后返回的promise是一样的,直接调用reject方法
  if(p2 === x) {
    reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if(x instanceof Promise2){   //x是promise对象
    //调用x的then方法去查看x的状态, 并传入成功和失败回调函数
    //如果返回的promise对象是同步的, 使用resolve 和 reject将参数传递给下一个.then使用
    //如果返回的promise对象是异步的,就将 此时的resolve和reject 传入异步的successCallback和failCallback中,等返回的promise有了执行结果后,去调用相应的回调函数,下一个.then就可以获取到值
    x.then(val => resolve(val), reason => reject(reason))
  }else{ //x是普通值
    resolve(x)
  }
}
module.exports = Promise2

总结

1、then方法的作用首先是 查看当前promise状态!!!然后再根据状态去确定要什么

2、在支持链式调用时,then方法查到当前promise是等待状态时,放入的内容理解:

then方法查到当前当前promise是成功时,会进行以下操作:

就是圈出来的代码块一中的内容, 也就是代码块一中的内容就是成功回调函数要做的事情。因此当前promise状态是等待时,我们只要创建一个匿名函数,将代码块中的内容放入匿名函数, 最后将匿名函数push进successCallback就可以了

3、圈出的地方异步执行的原因

在这里我们要比较 p2 和x是不是同一个promise对象,而只有new Promise2()执行完之后,才能拿到p2, 因此这里要等同步代码执行完之后再去执行, 所以将其变成异步