手写Promise

518 阅读6分钟

Promise核心逻辑实现

示例

//index.js
let promise = new Promise((resolve,reject) => {
    resolve('成功')//成功值
    reject('失败')
})

promise.then(value => {
    console.log(value)
}, reason => {
    console.log(reason)
})
//newPromise.js

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class NewPromise {
    constructor(executor) {
        executor(this.resolve,this.reject)
    }
    //promise状态
    status = PENDING
    value = undefined
    reason = undefined
    //值默认没有
    resolve = value => {
        //如果状态不是等待,阻止程序向下执行
        if(this.status !== PENDING)return
        this.status = FULFILLED
        this.value = value
    }
    //使用箭头函数的原因:直接调用一个普通函数,函数里面的this指向是undefined的
    reject = reason => {
        if(this.status !== PENDING)return
        //将状态更改为失败
        this.status = REJECTED
        this.reason = reason
    }
    //判断promise的状态,返回回调函数,需要传递value和reason
    then(successCallback,failCallback){
        //判断状态
        if(this.status === FULFILLED){
            successCallback(this.value)
        }else if(this.status === REJECTED){
            failCallback(this.reason)
        }
    }
}

module.exports = NewPromise

总结

  1. Promise 就是一个类,在执行这个类的时候,需要传递一个执行器进去,执行器会立即执行

    ...
    constructor(executor){
    	executor(this.resolve,this.reject)
    }
    //构造函数接收执行
    ...
    
  2. Promise 中有三种状态,分别为成功 fulfilled、失败 rejected、等待 pending

    一旦状态确定就不可更改

graph LR
pending --> fulfilled
pending --> rejected
  1. resolve和reject函数是用来更改状态的
resolve: fulfilled
reject: rejected
  1. then方法内部做的事情就判断状态,如果状态是成功,调用成功的回调函数,如果状态是失败。 调用失败回调函数 then方法是被定义在原型对象中的

    then作用:判断promise的状态,返回回调函数,需要传递value和reason

  2. then成功回调有一个参数value,表示成功之后的值,then失败回调有一个参数reason,表示失败后的原因

在Promise类中加入异步逻辑

示例,在执行器中加入异步代码

//index.js
let promise = new Promise((resolve,reject) => {
    setTimeout(() => {
        resolve('成功')
    },2000)
})

promise.then(value => {
    console.log(value)
}, reason => {
    console.log(reason)
})
//newPromise.js

class NewPromise {
    ...
    successCallback = undefined
    failCallback = undefined
	//默认是undefined
    ...
    resolve = value => {
        //如果状态不是等待,阻止程序向下执行
        if(this.status !== PENDING)return
        this.status = FULFILLED
        this.value = value
        //判断成功回调是否存在,如果存在 调用
        this.successCallback && this.successCallback(this.value)//在更改状态函数里执行成功/失败回调
    }
    //直接调用一个普通函数,函数里面的this指向是undefined的
    reject = reason => {
        if(this.status !== PENDING)return
        //将状态更改为失败
        this.status = REJECTED
        this.reason = reason
        //判断成功回调是否存在,如果存在 调用
        this.failCallback && this.failCallback(this.reason)//在更改状态函数里执行成功/失败回调
    }
    
     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
        }
    }
    ...
}

实现then方法多次调用添加多个处理函数

当有多个then时,会有多个成功和失败回调,此时应该将保存的回调函数变成一个数组,在状态更改时依次取出并且传递一个成功值/失败结果给成功回调/失败回调

//index.js
promise.then(value => {
    console.log(value)
})

promise.then(value => {
    console.log(value)
})

promise.then(value => {
    console.log(value)
})
//将保存的回调函数变成一个数组
successCallback = []
failCallback = []
...
//this.successCallback && this.successCallback(this.value)
while(this.successCallback.length){
            this.successCallback.shift()(this.value)
    //shift()方法会移除并回传阵列的第一个元素
    }
...
{
    this.successCallback.push(successCallback)
    this.failCallback.push(failCallback)
}
...

then方法的链式调用

  1. 当promise对象发生了链式调用时,应当在then方法里添加一个promise对象返回,以便于下一次then再次调用这个promise对象
//index.js
let promise = new Promise((resolve,reject) => {
    setTimeout(() => {
        resolve('成功')
    },2000)
})

promise.then(value => {
    console.log(value)
    return 100
    //链式调用then方法
}).then(value => {
    console.log(value)
    //value = 100
})
//newPromise.js
    then(successCallback, failCallback) {
        let promise2 = new NewPromise((resolve,reject) => {
            if (this.status === FULFILLED) {
                let x = successCallback(this.value)
                resolve(x)
                //传递的时一个普通值x的时候,调用resolve()方法,把这个普通纸传递给下一个promise2对象
            } else if (this.status === REJECTED) {
                failCallback(this.reason)
            } else {
                this.successCallback.push(successCallback)
                this.failCallback.push(failCallback)
            }
        })
        return promise2
        //调用了then方法会返回一个promise,以便于下一次then可以继续链式调用
    }
  1. 当链式调用的上一个then return的值不是一个普通值而是一个promise对象时

    • 判断 x 的值是普通值还是promise对象

    • 如果是普通值,直接调用resolve

    • 如果是promise对象,查看promsie对象返回的结果

    • 再根据promise对象返回的结果,决定调用resolve 还是调用reject

    //index.js
    let promise = new NewPromise((resolve,reject) => {
        resolve('成功')
    })
    
    function other(){
        return new NewPromise((resolve,reject) => {
            resolve('other')
        })
    }
    
    promise.then(value => {
        console.log(value)
        return other()//other是一个promise对象
    }).then(value => {
        console.log(value)
        //value = other
    })
    
    class NewPromise{
        ...
        then(successCallback, failCallback) {
            let promise2 = new NewPromise((resolve,reject) => {
                if (this.status === FULFILLED) {
                    let x = successCallback(this.value)
                    resolvePromise(x,resolve,reject)
                    //传递的时一个普通值x的时候,调用resolve()方法,把这个普通纸传递给下一个promise2对象,这里使用一个resolvePromise()方法去判断这个x是普通值还是promise对象
                } else if (this.status === REJECTED) {
                    failCallback(this.reason)
                } else {
                    this.successCallback.push(successCallback)
                    this.failCallback.push(failCallback)
                }
            })
            return promise2
        }
        ...
    }
    
        
    function resolvePromise(x,resolve,reject){
        if(x instanceof NewPromise){
            // x.then(value => resolve(value),reason => {reason})//查看状态
            x.then(resolve,reject)
        }else{
            resolve(x)
        }
    }
    

then方法链式调用识别Promise对象自返回

当then返回了当前的promise对象时,需要识别是否是自对象。

解决:在resolvePromise中判断当前传值x是否为当前priomise对象

//index.js
let p1 = promise.then(value => {
    console.log(value)
    return p1
})
p1.then(value=>{
    console.log(value)
},reason => {
    console.log(reason)
})
//promise的循环调用
function resolvePromise(promise2, x,resolve,reject){
    if(promise2 === x){
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if(x instanceof NewPromise){
        // x.then(value => resolve(value),reason => {reason})//查看状态
        x.then(resolve,reject)
    }else{
        resolve(x)
    }
}

捕获/处理错误

//在执行器中进行错误捕获
class NewPromise{
constructor(executor) {
        try{
            executor(this.resolve, this.reject)
        }catch(e){
            this.reject(e)
        }
    }
...
}

注意,上一个then方法的错误在下一个then中捕获到

同理,不止要在执行器中进行错误捕获,在状态改变之后进行也要进行错误捕获

then(successCallback, failCallback) {
        let promise2 = new NewPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    try {
                        let x = successCallback(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                try {
                    failCallback(this.reason)
                } catch (e) {
                    reject(e)
                }
            } else {
                this.successCallback.push(()=>{
                    successCallback()
                })
                this.failCallback.push(() => {
                    failCallback()
                })
                //要对回调函数进行处理
            }
        })
        return promise2
    }

将then方法的参数变成可选参数

当then不传递任何参数时,最新一个then获取的value还是上一个最新then传递的参数

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

//相当于
promise
    .then(value => value)
    .then(value => value)
//一层层向后进行传递相同的内容
    .then(value => console.log(value))
//NewPromise.js
then(successCallback, failCallback){
	successCallback = successCallback ? successCallback : value => value
    failCallback = failCallback ? failCallback : reason => reason
}

Promise.all()

  1. 返回值是Promise对象
  2. 参数为数组,数组中可以传递普通值和promise对象
  3. 可以解决异步并发,允许按照异步API调用顺序得到异步API的执行结果
  static all (array) {
    let result = [];
    let index = 0;
    return new MyPromise((resolve, reject) => {
      function addData (key, value) {
        result[key] = value;
        index++;
        if (index === array.length) {
          resolve(result);
        }
      }
      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        if (current instanceof MyPromise) {
          // promise 对象
          current.then(value => addData(i, value), reason => reject(reason))
        }else {
          // 普通值
          addData(i, array[i]);
        }
      }
    })
  }

promise.resolve()

  static resolve (value) {
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value));
      //普通值:创建promise对象,传递一个执行器,使用resolve方法将value进行返回
  }

Promise.finally()

  1. 无论当前Promise对象的执行结果是成功还是失败,finally()方法当中的回调函数始终会被执行一次

  2. 在finally()方法的后面可以链式调用then方法来拿到Promise对象最终返回的结果

  3. 不是静态方法,要定义在原型对象身上

    finally (callback) {
        return this.then(value => {
          return MyPromise.resolve(callback()).then(() => value);
          //需要借助resolve,不管是普通值还是promise对象,都使用resolve转化为promise对象
          //再使用then地成功回调方法返回一个value
          //这样就可以等待callback()的返回值,再去执行return value
          //避免setTimeout没走完就返回了value
        }, reason => {
          return MyPromise.resolve(callback()).then(() => { throw reason })
        })
      }
    
    //index.js
    function p1 () {
      return new MyPromise(function (resolve, reject) {
        setTimeout(function () {
          resolve('p1')
        }, 2000)
      })
    }
    function p2 () {
      return new MyPromise(function (resolve, reject) {
        // reject('失败')
        resolve('p2 finally');  
      })
    }
    
    
    p2().finally(()=> {
      console.log('finally')
      return p1()
    }).then(value => {
      console.log(value)
      //没等两秒之后就已经输出了
    },reason => {
      console.log(reason)
    })
    
    

    Promise.catch()

    链式调用catch去处理reject()失败的情况

      catch (failCallback) {
        return this.then(undefined, failCallback)
        //不传递成功回调,只传递失败回调
        //return便于之后使用promise的其它方法
      }