手动实现一个ES6的promise函数

简单介绍:Promise函数是ES6一个重要的部分

new promise对象

promise是一个构造函数,自身有rejectresolverace方法,原型链上有thencatch方法。用new出来一个promise对象会自带这些方法。


    let p1 =  new  Promise((resolve, reject) => {

        setTimeout(() => {

            let number =  parseInt(Math.random() *  100)

            if (number >  50) {

                resolve(number)

            } else {

                reject(number)

            }
            console.log('数据11111111111111111111111')
        }, 3000)

    }) 
    // 3秒后 打印了 数据11111111111111111111111
    

在这里只是new了一个对象,并没有调用它,传进去的函数就已经执行了,这是需要注意的一个细节。所以用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数

then方法

then可以接收两个参数,是函数(即resolvereject方法),并且会拿到刚才在p1中调用resolvereject时传的的参数。----在这里resolvereject相当于回调函数。

p1.then(resolve => {

    console.log('随机数大于50,resolve结果:', resolve)

}, reject => {

    console.log('随机数小于50,reject结果:', reject)

})
catch方法

其实它和then的第二个参数一样,用来指定reject的回调。另外一个作用:在执行resolve时(then中的第一个参数),如果抛出异常了(代码语法错误或者单词拼写错误),那么并不会报错卡死js,而是会进到这个catch方法中。

p1.then(resolve => {

    console.log('随机数大于50,resolve结果:', resolve)

})
.catch(error => {

    console.log('随机数小于50,error结果:', error)

})

手动实现:一个手写模拟Promise函数机制

因为JS无法操作底层的微任务,所以这里用setTimeout来模拟
首先要明白Promise函数的两个特点:

  1. Promise对象的状态不受外界影响

    • pending(进行中)
    • fulfilled(已成功)
    • rejected(已失败)
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果
    状态改变,只有两种情况

    • pending变为fulfilled
    • pending变为rejected

只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时候就成为resolved(已定型)。
PS:提一下这个方面Promise与实践Event的区别:
如果Promise对象状态改变已经发生了,再对Promise添加回调函数,也会立即得到状态已经改变的结果。而事件Event则不同,它是:如果错过了事件,再去监听,是得不到结果的。

先实现一个简易版的
function  MyPromise (executor) {

    // 保存当前的 Promise 实例

    let self =  this

    // 初始化当前的 Promise 实例

    self.value =  null

    self.error =  null

    self.status =  PENDING




    // 成功的回调函数初始化
    // 为什么要写成数组,

    // 因为有时候要绑定多个回调函数,
    // 想要在 resolve() 之后一起执行

    // 需要将 onFulfilled 和 onRejected 改为数组,
    // 调用 resolve 时将其中的方法拿出来一一执行即可。

    self.onFulfilledCallbacks = []

    // 失败的回调函数初始化

    self.onRejectedCallbacks = []



    // 处理成功以后的回调函数

    const  resolve  = (value) => {

    // 判断 Promise 实例的状态,执行 resolve
    // 或者reject 函数之前,状态应该是pending·

    // Promise 实例的状态一旦改变 则凝固,不能再变

    if (self.status !==  PENDING) return

    setTimeout(() => {

    // 改变状态 为 fulfilled

    self.status =  FULFILLED

    self.value = value // 这个是要传递出去的值

    // resolve 成功时执行这个回调

    // console.log('实例里面成功的函数')

    // 这里的 onFulfilled 是经过 then 方法里面赋值的

    self.onFulfilledCallbacks.forEach(
    
            (callback) => callback(self.value)
            
            )

        })

    }



    // 处理失败后的回调函数

    const  reject  = (error) => {

    if (self.status !==  PENDING) return

    setTimeout(() => {

    self.status =  REJECTED

    self.error = error

    // console.log('实例里面失败的函数')

    // reject 失败后执行这个回调

    // 这里的 onRejected 是经过 then 方法里面赋值的

    // self.onRejected(self.error)

    self.onRejectedCallbacks.forEach(
    
            (callback) => callback(self.error)
            
        )

        })

    }

    // 调用者传进来的函数参数

    executor(resolve, reject)

}
实现 then 方法
// Promise.then() 方法的主要作用是 
// 将成功或者失败的函数在实例状态
// 还为 pending 的时候赋值给 Promise 实例

MyPromise.prototype.then = function (onFulfilled, onRejected) {

    // 如果 调用者 Promise 实例的状态为 pending,
    // 则将then方法接收的两个分别处理成功后和失败后
    // 的函数赋值给 Promise 实例

    if (this.status ===  PENDING) {

        // this.onFulfilled = onFulfilled

        // this.onRejected = onRejected

        this.onFulfilledCallbacks.push(onFulfilled)

        this.onRejectedCallbacks.push(onRejected)

    } else  if (this.status ===  FULFILLED) {

        // 这一步 基本不会被执行

        // 如果状态是成功的,直接执行成功的回调函数,
        // 并将成功的值传入

        console.log('then里面成功的函数')

        onFulfilled(this.value)

    } else {

        // 这一步基本不会被执行

        // 如果状态是失败的,直接执行失败的回调函数,
        // 并将失败的值传入

        console.log('then里面失败的函数')

        onRejected(this.error)

    }

    console.log('this: ', this) // MyPromise 实例

    // 这里是一个问题点 
    // 因为总是会返回第一个 Promise 实例
    // 不能实现链式调用 then

    return  this

}
这里有三个问题点
  1. 这里的 then 方法返回的是 this ,永远都会是第一个调用的 Promise 函数实例,所以做不到链式调用
  2. 如果调用者调用then方法的时候并没有传参数,这种情况没有处理。
  3. 加入then方法中的回调函数执行后返回的结果(也就是上面的x)是一个Promise的实例,直接给resolve了,这是不希望看到的。

先对3的情况写个处理函数

function resolvePromise(bridgePromise, x, resolve, reject) {

    //如果x是一个promise

    if (x instanceof MyPromise) {

        // 拆解这个 promise ,直到返回值不为 promise 为止

        if (x.status ===  PENDING) {

            x.then(y => {

            resolvePromise(bridgePromise, y, resolve,                   reject)}, error => {

            reject(error);

            });

        } else {

            x.then(resolve, reject);

        }

    } else {

        // 非 Promise 的话直接 resolve 即可

        resolve(x);

    }

}

所以改进后的 then方法为:

MyPromise.prototype.then =  function (onFulfilled, onRejected) {

    // 首先处理then中的两个参数不传的情况

    // 成功回调不传给它一个默认函数

    onFulfilled = typeof onFulfilled ===  'function'  ?
    onFulfilled :
    value  => value

    // 对于失败回调直接抛错

    onRejected =  typeof onRejected ===  'function'  ?
    onRejected : 
    error  => { throw error }



    let bridgePromise

    let self =  this

    if (self.status ===  PENDING) {

        return bridgePromise = new MyPromise(
            (resolve,reject) => {

            self.onFulfilledCallbacks.push((value) => {

                try {

                    // 要拿到 then 中回调返回的结果。

                    let x =  onFulfilled(value)

                    resolvePromise(bridgePromise, x, resolve, reject)

                } catch (e) {

                    reject(e)

                }

            })

        self.onRejectedCallbacks.push((error) => {

                try {

                    let x =  onRejected(error)

                    resolvePromise(bridgePromise, x, resolve, reject)

                } catch (e) {

                    reject(e)

                }

            })

        })

    }

}

上面的只写了进行中的状态下,
还有成功时候(FULFILLED)的then方法逻辑:

if (self.status ===  FULFILLED) {

    return bridgePromise =  new  MyPromise((resolve, reject) => {

        try {

            // 状态变为成功,会有相应的 self.value

            let x \=  onFulfilled(self.value);

            // 暂时可以理解为 resolve(x),后面具体实现中有拆解的过程

            resolvePromise(bridgePromise, x, resolve, reject);

        } catch (e) {

            reject(e)

        }

    })

}

如果是失败的状态(REJECTED)下then函数逻辑:

if (self.status ===  REJECTED) {

    return bridgePromise =  new  MyPromise((resolve, reject) => {

        try {

            // 状态变为失败,会有相应的 self.error

            let x \=  onRejected(self.error);

            resolvePromise(bridgePromise, x, resolve, reject);

        } catch (e) {

            reject(e);

        }

    })

}

到目前为止,基本上实现了then方法的链式调用,
现在来测试一下:

// 示例

function  MP1 () {

    let p1 =  new  MyPromise(function (resolve, reject) {

        let num1 =  parseInt(Math.random() *  100)

        setTimeout( () => {

            if (num1 >=  50) {

                resolve(num1)

            } else {

                reject(num1)

            }

        }, 3000)

    })

    return p1

}

function  MP2 () {

    let p2 =  new  MyPromise(function (resolve, reject) {

        let num2 =  parseInt(Math.random() *  1000)

        setTimeout(() => {

            if (num2 >=  500) {

                resolve(num2)

            } else {

                reject(num2)

            }

        }, 3000)

    })

    return p2

}

MP1().then(value => {

        console.log('大于或者等于50的num1值为: ', value)

        return  MP2()

    }, error  => {

        console.log('小于50的num1为: ', error)

        return  MP2()

})

.then(value => {

    console.log('大于或者等于500的num2值为: ', value)

}, error => {

    console.log('小于500的num2为: ', error)

 })
 
 
 // (3秒之后) 大于或者等于50的num1值为:  83
 // (又过了3秒之后)小于500的num2为:  231
最后,错误捕获及冒泡机制 catch 方法

这个方法实现就简单了,它只是then方法的语法糖,更重要的是理解其中错误冒泡的机制,即中途一旦发生错误,可以在最后用 catch 捕获错误。
一旦其中有一个PENDING状态的 Promise 出现错误后状态必然会变为失败, 然后执行 onRejected函数,而这个 onRejected 执行又会抛错,把新的 Promise 状态变为失败,新的 Promise 状态变为失败后又会执行onRejected........就这样一直抛下去,直到用catch 捕获到这个错误,才停止往下抛。
实现catch方法:

Promise.prototype.catch = function (onRejected) { 
    return  this.then(null, onRejected) 
}

目前,针对Promise函数的特点都做了基本的实现,
Promise函数还有一些方法,比如:

  • Promise.prototype.finally()
  • Promise.all()
  • Promise.race()
  • Promise.resolve()
  • Promise.reject()
  • Promise.try
  • ....

暂时就没有做进一步实现了

你可能感兴趣的:(javascript前端es6)