手写promise源码

98 阅读6分钟

手写promise源码

promise状态pending

首先我们需要了解,promise有三个状态

  • promise状态只能改变一次,只有一个结果数据
  • pending----->fulfilled::成功数据value
  • pending----->rejected::失败原因reason

通过我们平时使用promise对象,我们可以了解到,Promise构造函数返回一个Promise对象实例,这个返回的Promise对象具有一个then 方法。在 then 方法中,调用者可以定义两个参数,分别是onfulfilled和onrejected,它们都是函数类型的参数。其中,onfulfilled通过参数可以获取Promise对象经过resolve处理后的值,onrejected可以获取Promise对象经过reject处理后的值。通过这个值,我们来处理异步操作完成后的逻辑。

对此,我们可以写出来以下代码结构:

function Mypromise(executor) {
        this.status = 'pending'
        this.value = undefined
        this.reason = undefined
        let resolve = (value)=>{
           this.value = value
        }
        let reject = (reason)=>{
            this.reason = reason
        }
        executor(resolve,reject)
    }
    
Mypromise.prototype.then=function(onFulfilled,onRejected){
    onFulfilled(this.value)
    onRejected(this.reason)
}

因为promise的状态是不可逆的,所以我们需要判断每次执行resolve的状态:

function Mypromise(executor) {
    this.status = 'pending'
    this.value = undefined
    this.reason = undefined

    let resolve = (value) => {
        if (this.status === 'pending') {
            this.value = value
            this.status = 'fulfilled'
        }

    }
    let reject = (reason) => {
        if (this.status === 'pending') {
            this.reason = reason
            this.status = 'rejected'
        }

    }
    executor(resolve, reject)
}
// 这里暂时添加函数的原型给2个回调函数默认值,防止不传入函数回调报错
Mypromise.prototype.then = function (onFulfilled = Function.prototype, onRejected = Function.prototype) {
    if (this.status === 'fulfilled') {
        onFulfilled(this.value)
    }
    if (this.status === 'rejected') {
        onRejected(this.reason)
    }
}

到这里我们就完成了简单的promise的实现。但是我们的代码实现不了异步的回调功能:

const p1 = new MyPromise((resolve,reject)=>{
    setTimeout(()=>{
        resolve('success')
    },2000)
    
})
p1.then(y=>{
    console.log(y);
})

promise异步实现完善

上边无法实现异步回调的原因是,正常来讲,上述代码会在 2s 后输出 success,但是现在,代码并没有输出任何信息。这是为什么呢? 原因很简单,因为我们的实现逻辑全是同步的。上述代码在实例化一个Promise的构造函数时,会在setTimeout逻辑中调用resolve,也就是说,2s后才会调用resolve方法,更改Promise实例状态。而结合我们的实现,then方法中的onfulfilled是同步执行的,它在执行时thisstatus仍然为pending,并没有做到“2s后再执行onfulfilled”。 那该怎么办呢?我们似乎应该在合适的时间去调用onfulfilled方法,这个合适的时间应该是开发者调用resolve 的时刻,那么我们先在状态(status)为pending时把开发者传进来的onfulfilled方法存起来,再在 resolve 方法中执行即可。

修改代码如下:

function Mypromise(executor) {
    this.status = 'pending'
    this.value = undefined
    this.reason = undefined
    this.onFulfilledFunc;
    this.onRejectedFunc;
    let resolve = (value) => {
        if (this.status === 'pending') {
            this.value = value
            this.status = 'fulfilled'
            this.onFulfilledFunc(this.value)
        }

    }
    let reject = (reason) => {
        if (this.status === 'pending') {
            this.reason = reason
            this.status = 'rejected'
            this.onRejectedFunc(this.reason)
        }

    }
    executor(resolve, reject)
}
// 这里暂时添加函数的原型给2个回调函数默认值,防止不传入函数回调报错
Mypromise.prototype.then = function (onFulfilled = Function.prototype, onRejected = Function.prototype) {
    if (this.status === 'fulfilled') {
        onFulfilled(this.value)
    }
    if (this.status === 'rejected') {
        onRejected(this.reason)
    }
    if(this.status === 'pending'){
        this.onFulfilledFunc = onFulfilled
        this.onRejectedFunc = onRejected
    }
}
}

但是伴随这一个新的问题出现了,下边这种情况下应该输出2个success但是最后却只输出了最后一个success,因为我们then方法调用后会把上一个的onFulfilled或onRejected覆盖掉,对于此情况我们需要把他们都放到一个数组中,当我们resolve的时候循环遍历执行即可:

function Mypromise(executor) {
    this.status = 'pending'
    this.value = undefined
    this.reason = undefined
    this.onFulfilledFunc = []
    this.onRejectedFunc = []
    let resolve = (value) => {
        if (this.status === 'pending') {
            this.value = value
            this.status = 'fulfilled'
            this.onFulfilledFunc.forEach(func=>func())
        }

    }
    let reject = (reason) => {
        if (this.status === 'pending') {
            this.reason = reason
            this.status = 'rejected'
            this.onRejectedFunc.forEach(func=>func())
        }

    }
    executor(resolve, reject)
}
// 这里暂时添加函数的原型给2个回调函数默认值,防止不传入函数回调报错
Mypromise.prototype.then = function (onFulfilled = Function.prototype, onRejected = Function.prototype) {
    if (this.status === 'fulfilled') {
        onFulfilled(this.value)
    }
    if (this.status === 'rejected') {
        onRejected(this.reason)
    }
    if(this.status === 'pending'){
        this.onFulfilledFunc.push(()=>{
            onFulfilled(this.value)
        })
        this.onRejectedFunc.push(()=>{
            onRejected(this.reason)
        })
    }
}

当我们执行下面代码时,应先打印出 111 ,在打印success,但是我们的promise先打印除了success才出现111,所以我们需要把then的执行改为微任务队列,可以使用借助setimeout也可以queueMicrotask来执行微任务

const p1 = new MyPromise((resolve,reject)=>{
    resolve('success')
    
})
p1.then(y=>{
    console.log(y);
})
p1.then(y=>{
    console.log(y);
})

console.log(111);

修改完成后的代码如下:

function Mypromise(executor) {
    this.status = 'pending'
    this.value = undefined
    this.reason = undefined
    this.onFulfilledFunc = []
    this.onRejectedFunc = []
    let resolve = (value) => {
        if (this.status === 'pending') {
            this.value = value
            this.status = 'fulfilled'
            this.onFulfilledFunc.forEach(func => func())
        }

    }
    let reject = (reason) => {
        if (this.status === 'pending') {
            this.reason = reason
            this.status = 'rejected'
            this.onRejectedFunc.forEach(func => func())
        }

    }
    executor(resolve, reject)
}
// 这里暂时添加函数的原型给2个回调函数默认值,防止不传入函数回调报错
Mypromise.prototype.then = function (onFulfilled = Function.prototype, onRejected = Function.prototype) {
    if (this.status === 'fulfilled') {
        setTimeout(() => {
            onFulfilled(this.value)
        })
    }
    if (this.status === 'rejected') {
        setTimeout(() => {
            onRejected(this.reason)
        })
    }
    if (this.status === 'pending') {
        this.onFulfilledFunc.push(() => {
            setTimeout(() => {
                onFulfilled(this.value)
            })

        })
        this.onRejectedFunc.push(() => {
            setTimeout(() => {
                onRejected(this.reason)
            })
        })
    }
}

到此我们已经简单的实现了promise的同步和异步使用了。

promise的链式调用

promise的链式调用上一个的then方法需要返回一个promise对象,我们才可以调用,我们在then方法里边执行的onRejected和onFulfilled的返回值就是下一个then方法所需要的resolve的值,所以我们可以这样做:

function Mypromise(executor) {
    this.status = 'pending'
    this.value = undefined
    this.reason = undefined
    this.onFulfilledFunc = []
    this.onRejectedFunc = []
    let resolve = (value) => {
        if (this.status === 'pending') {
            this.value = value
            this.status = 'fulfilled'
            this.onFulfilledFunc.forEach(func => func())
        }

    }
    let reject = (reason) => {
        if (this.status === 'pending') {
            this.reason = reason
            this.status = 'rejected'
            this.onRejectedFunc.forEach(func => func())
        }

    }
    executor(resolve, reject)
}
// 这里暂时添加函数的原型给2个回调函数默认值,防止不传入函数回调报错
Mypromise.prototype.then = function (onFulfilled = Function.prototype, onRejected = Function.prototype) {
    
    const promise2 = new Mypromise((resolve,reject)=>{
        let x;
        if (this.status === 'fulfilled') {
            setTimeout(() => {
                x = onFulfilled(this.value)
                resolve(x)
            })
        }
        if (this.status === 'rejected') {
            setTimeout(() => {
                x = onRejected(this.reason)
                reject(x)
               
            })
        }
        if (this.status === 'pending') {
            this.onFulfilledFunc.push(() => {
                setTimeout(() => {
                    x = onFulfilled(this.value)
                    resolve(x)
                })
    
            })
            this.onRejectedFunc.push(() => {
                setTimeout(() => {
                    x = onRejected(this.reason)
                    reject(x)
                })
            })
        }
    })
    return promise2
}

但是promise里边也有肯能继续返回一个promise所以我们需要对x做判断,如下边这样:

const p1 = new MyPromise((resolve,reject)=>{
    resolve(new new MyPromise((resolve,reject)=>{
             resolve('success')
    }))
})
p1.then(y=>{
    console.log(y);
})
p1.then(y=>{
    console.log(y);
})

所以我们需要写一个函数对x进行判断:

const resolvePromise = function(p2,x,resolve,reject){
    // 如果promise和 result相等会陷入死循环      
       if(p2===x){
       return reject(new TypeError('type error'))
       }
       let consumed = false
       if((typeof x === 'function') || (typeof x === 'object' && x !== null)){
         try{
         let thenable = x.then
         if(typeof thenable === 'function'){
            thenable.call(x,(y)=>{
                if(consumed) return
                consumed = true
                 resolvePromise(p2,y,resolve,reject)
            },err=>{
                if(consumed) return
                consumed = true
                 reject(err)
            })
         }else{
            if(consumed) return
            consumed = true
            resolve(x)
         }
         }catch(err){
            if(consumed) return
            consumed = true
             reject(err)
         }
       }else{
        resolve(x)
       }
}
}

完整的代码如下:

function Mypromise(executor) {
    this.status = 'pending'
    this.value = undefined
    this.reason = undefined
    this.onFulfilledFunc = []
    this.onRejectedFunc = []
    let resolve = (value) => {
        if (this.status === 'pending') {
            this.value = value
            this.status = 'fulfilled'
            this.onFulfilledFunc.forEach(func => func())
        }

    }
    let reject = (reason) => {
        if (this.status === 'pending') {
            this.reason = reason
            this.status = 'rejected'
            this.onRejectedFunc.forEach(func => func())
        }

    }
    // 防止promise初始化时报错
    try{
        executor(resolve, reject)
    }catch(e){
        reject(e)
    }
   
}
// 这里暂时添加函数的原型给2个回调函数默认值,防止不传入函数回调报错
Mypromise.prototype.then = function (onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : data => data
    onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }
    const promise2 = new Mypromise((resolve,reject)=>{
        let x;
        if (this.status === 'fulfilled') {
            setTimeout(() => {
                try{
                    x = onFulfilled(this.value)
                    resolvePromise(promise2,x,resolve,reject)
                }catch(e){
                       reject(e)
                }
               
            })
        }
        if (this.status === 'rejected') {
            setTimeout(() => {
                try{
                    x = onRejected(this.reason)
                    resolvePromise(promise2,x,resolve,reject)
                }catch(e){
                    reject(e)
                }
            })
        }
        if (this.status === 'pending') {
            this.onFulfilledFunc.push(() => {
                setTimeout(() => {
                    try{
                        x = onFulfilled(this.value)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                })
            })
            this.onRejectedFunc.push(() => {
                setTimeout(() => {
                    try{
                        x = onRejected(this.reason)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                })
            })
        }
    })
    return promise2
}

const resolvePromise = function(p2,x,resolve,reject){
    // 如果promise和 result相等会陷入死循环
      
       if(p2===x){
       return reject(new TypeError('type error'))
       }
       let consumed = false
       if((typeof x === 'function') || (typeof x === 'object' && x !== null)){
         try{
         let thenable = x.then
         if(typeof thenable === 'function'){
            thenable.call(x,(y)=>{
                if(consumed) return
                consumed = true
                 resolvePromise(p2,y,resolve,reject)
            },err=>{
                if(consumed) return
                consumed = true
                 reject(err)
            })
         }else{
            if(consumed) return
            consumed = true
            resolve(x)
         }
         }catch(err){
            if(consumed) return
            consumed = true
             reject(err)
         }
       }else{
        resolve(x)
       }
}

测试用例:

npm i promises-aplus-tests -g 运行命令 promises-aplus-tests 文件名称

用例编写

Mypromise.defer = Mypromise.deferred = function(){
    let dfd = {}
    dfd.promise = new Mypromise((resolve,reject)=>{
        dfd.resolve = resolve
        dfd.reject = reject
    })
    return dfd
}
module.exports = Mypromise