个人(雾)手写Promise

78 阅读7分钟

前言

手写Promise可谓是JS码农们的一个必备技能,熟练手写之后,不但能进一步较好地熟悉JS诸多机制,而且还能锻炼对代码编写细节的操控能力。互联网上手写Promise的范例超级多,好多优秀选手对它可谓是熟练于心呀。不多说,今天我就借鉴各位江湖高手的手法,总结一下我个人对于Promise这门武林秘籍的看法。

源码:

(需要请自取,我这个版本肯定也有一些细节问题没有考虑到,比如一些安全检测)

class Promise {
    static PENDING = 'PENDING';
    static REJECTED = 'REJECTED';
    static FULFILLED = 'FULFILLED';
    constructor(callback) {
        //如果不是回调函数
        if (!(callback instanceof Function)) callback = () => {}
        this.state = Promise.PENDING
        this.result = null
        this.onFulfilledcallbacks = []
        this.onRejectedcallbacks = []
        const resolve = (data) => {
            if (this.state == Promise.PENDING) {
                this.state = Promise.FULFILLED
                this.result = data
            }
            if (this.onFulfilledcallbacks) {
                setTimeout(() => {
                    this.onFulfilledcallbacks.forEach(callback => {
                        callback(data)
                    })
                });
            }
        }
        const reject = (data) => {
            if (this.state == Promise.PENDING) {
                this.state = Promise.REJECTED
                this.result = data
                if (this.onRejectedcallbacks) {
                    setTimeout(() => {
                        this.onRejectedcallbacks.forEach(callback => {
                            callback(data)
                        })
                    })
                }
            }
        }
        try {
            callback(resolve, reject)
        } catch (e) {
            reject(e)
        }
    }

    then(onResolve, onReject) {
        return new Promise((resolve, reject) => {
            const callback = (type) => {
                try {
                    const result = type(this.result)
                    if (result instanceof Promise) {
                        //如果是 解构Promise对象
                        result.then((res) => {
                            resolve(res)
                        }, (err) => {
                            reject(err)
                        })
                    } else {
                        resolve(result)
                    }
                } catch (e) {
                    reject(e)
                }
            }
            if (this.state == Promise.REJECTED) {
                setTimeout(() => {
                    callback(onReject)
                });
            }
            if (this.state == Promise.FULFILLED) {
                setTimeout(() => {
                    callback(onResolve)
                })
            }
            if (this.state == Promise.PENDING) {
                this.onFulfilledcallbacks.push(() => {
                    callback(onResolve)
                })
                this.onRejectedcallbacks.push(() => {
                    callback(onReject)
                })
            }
        })
    }

    static all(promises) {
        return new Promise((resolve, reject) => {
            const arr = []
            let index = 0
            for (let promise of promises) {
                //如果元素是Promise类型
                const currentIndex = index
                if (promise instanceof Promise) {
                    promise.then((r) => {
                        arr[currentIndex] = r
                        if (Object.keys(arr).length == promises.length) resolve(arr)
                    }, (e) => {
                        reject(e)
                    })
                } else {
                    arr[currentIndex] = promise
                    if (Object.keys(arr).length == promises.length) resolve(arr)
                }
                index++
            }
        })
    }

    static race(promises){
        return new Promise((resolve,reject)=>{
            for(let promise of promises){
                if(promise instanceof Promise){
                    promise.then((r)=>{
                        resolve(r)
                    },(e)=>{
                        reject(e)
                    })
                }
                else {
                    resolve(promise)
                }
            }
        })
    }

    static allsettled(promises){
        return new Promise((resolve,reject)=>{
            const arr=[]
            let index=0
            for(let promise of promises){
                //保存当前块index的值
                const currentIndex=index
                if(promise instanceof Promise){
                    promise.then(()=>{
                        arr[currentIndex]={
                            status:promise.state,
                            value:promise.result,
                        }
                        if(Object.keys(arr).length==promises.length)resolve(arr)
                    },()=>{
                        arr[currentIndex]={
                            status:promise.state,
                            reason:promise.result,
                        }
                        if(Object.keys(arr).length==promises.length)resolve(arr)
                    })
                }
                else{
                    arr[currentIndex]={
                        status:Promise.FULFILLED,
                        value:promise,
                    }
                    if(Object.keys(arr).length==promises.length)resolve(arr)
                }
                index++
            }
        })
    }

    //任意的promise完成,则返回这个完成的promise的信息
    static any(promises){
        return new Promise((resolve,reject)=>{
            for(let promise of promises){
                if(promise instanceof Promise){
                    promise.then((r)=>{
                        resolve(r)
                    })
                }else{
                    resolve(promise)
                    break
                }
            }
            reject('AggregateError: All promises were rejected')
        })
    }

    static resolve(data) {
        return new Promise((resolve, reject) => {
            if(data instanceof Promise){
                data.then((r)=>{
                    resolve(r)
                },(e)=>{
                    reject(e)
                })
            }else{
                resolve(data)
            }
        })
    }

    static reject(data) {
        return new Promise((resolve, reject) => {
            reject(data)
        })
    }
}

怎么样,源码是不是非常多啊,但是不急,咱们看的时候可以先从构造函数看,然后再是resolve、reject、then等等。主要呢还是要弄懂每一个方法的基本作用,这样手写下来其实就不难了。

1.constructor

constructor是类的构造函数,我们创建(new)一个类的实例的时候,就会运行这个构造函数。想一想Promise的构造函数的参数是怎么样的呢?

new Promise((resolve,reject)=>{...})

是的,就是这样的形式,因此我们需要在构造函数上添加一个形参(这里叫callback),作为回调函数,表示未来我们会在构造函数中调用在这个位置传入的函数(通过new)。

constructor(callback) {
        //如果不是回调函数
        if (!(callback instanceof Function)) callback = () => {}
        this.state = Promise.PENDING
        this.result = null
        this.onFulfilledcallbacks = []
        this.onRejectedcallbacks = []
        const resolve = (data) => {
            if (this.state == Promise.PENDING) {
                this.state = Promise.FULFILLED
                this.result = data
            }
            if (this.onFulfilledcallbacks) {
                setTimeout(() => {
                    this.onFulfilledcallbacks.forEach(callback => {
                        callback(data)
                    })
                });
            }
        }
        const reject = (data) => {
            if (this.state == Promise.PENDING) {
                this.state = Promise.REJECTED
                this.result = data
                if (this.onRejectedcallbacks) {
                    setTimeout(() => {
                        this.onRejectedcallbacks.forEach(callback => {
                            callback(data)
                        })
                    })
                }
            }
        }
        try {
            callback(resolve, reject)
        } catch (e) {
            reject(e)
        }
    }

resolve、reject

然后还应该有resolve、reject这样的函数,用于将Promise对象的状态改变为“成功”或“失败”,并将它们的参数设置为Promise对象的result值。

 这里补一下Promise的状态的概念,Promise有三种状态,分别是“阻塞”、“成功”和“失败”,如果我们创建Promise实例的时候没有调用resolve和reject改变状态,那么Promise对象的状态就是默认值“阻塞”,而当Promise对象变为“成功”或“失败”的时候,它的状态是不可逆的,也就是不能再变成其他状态,这里在resolve和reject中加一个判断就是了,只有“阻塞”状态才能正确的reject和resolve。附图如下:

image.png

Promise对象有自己的状态state和result,分别表示Promise的当前状态和结果,其中结果是执行resolve(或者reject)时的参数

    this.state = Promise.PENDING
    this.result = null

2.then

我们知道,Promise使用then实现了链式调用,形如promise.then(...).then(...)。 “.”运算符使得我们摆脱了回调地狱的境遇。

对于Promise的方法,只要返回Promise对象,我们都是使用的return new Promise(...)这样的形式,实现起来既简单又高效,而且对于这里的then方法,它也的确应该返回的新的Promise。

为什么呢?我们知道then方法接受两个回调函数作为参数,第一个参数是Promise对象状态成功时要调用的函数,我这里称onResolve,第二个自然是状态失败时调用的函数,我这里称onReject。但不管它们谁最后被调用,只要它们返回了成功的值(也即成功的Promise或者非Promise值),then也应该返回成功的Promise;而执行它们时如果返回了失败的Promise,then最后也会返回失败的Promise

最终,它们返回的期约的result(或是非Promise值)也会作为then返回的Promise的result的值。这就决定了,它们必须返回新的Promise,这个新的Promise的状态应该由then中的回调的执行成功或失败来决定,并且,它的result值由执行回调的返回值决定

(传送门-->什么是回调地狱,如何用Promise解决回调地狱,看完这篇你就明白了。 - 掘金 (juejin.cn)) then方法对于调用者Promise对象的三种状态,有不同的处理方式。比如在对象“阻塞”时,会将其放入到对象自身的数据解构中保存,这里是数组

    this.onFulfilledcallbacks = []
    this.onRejectedcallbacks = []

等到Promise对象状态改变时,会根据成功或失败分别调用对应数组里面保存的回调

    then(onResolve, onReject) {
        return new Promise((resolve, reject) => {
            const callback = (type) => {
                try {
                    const result = type(this.result)
                    if (result instanceof Promise) {
                        //如果是 解构Promise对象
                        result.then((res) => {
                            resolve(res)
                        }, (err) => {
                            reject(err)
                        })
                    } else {
                        resolve(result)
                    }
                } catch (e) {
                    reject(e)
                }
            }
            if (this.state == Promise.REJECTED) {
                setTimeout(() => {
                    callback(onReject)
                });
            }
            if (this.state == Promise.FULFILLED) {
                setTimeout(() => {
                    callback(onResolve)
                })
            }
            if (this.state == Promise.PENDING) {
                this.onFulfilledcallbacks.push(() => {
                    callback(onResolve)
                })
                this.onRejectedcallbacks.push(() => {
                    callback(onReject)
                })
            }
        })
    }

3.resolve(静态方法)

 之后的方法,我都只会介绍一下它们的作用。

Promise.resolve会依据传入的参数返回一个成功或失败的Promise对象。

  1. 从参数中得到的是一个Promise对象

     resolve会等待这个Promise对象改变状态,将它的result作为resolve()返回的Promise对象的result,并且如果它失败了,会返回reject
    
  2. 从参数中得到的是一个非Promise值

     resolve会将其封装成一个Promise对象(new Promise),将其值作为这个对象的result,状态为成功
     
    

4.reject(静态方法)

和resolve类似,但是不管参数是什么,它直接返回一个失败的Promise对象,并将参数直接当作对象的result。

5.all(静态方法)

参数是一个可迭代对象(具有Symbol.iterator属性),一般传入数组,all会遍历数组元素。过程中只要第一次出现一个Promise对象改变为失败状态,那么all最终返回一个失败的Promise对象,并且result的值是这个第一个失败的Promise的result。只有所有的元素是一个成功的期约(包括非Promise值)时,all才返回一个成功的Promise对象,并且,result是这些元素按下标顺序排列的,由Promise的result值和非Promise值组成的数组。

6.race(静态方法)

参数和all相同,唯一的区别是数组元素只要出现了一个成功(或是非Promise值)或失败的Promise对象,那么就会返回这个值“构成”的Promise对象

7.any(静态方法)

参数同上,只要出现一个成功的Promise(或是非Promise值),返回一个成功的Promise,它的result不用我说也应该知道是啥吧?

8.allsettled(静态方法)

参数同上,数组元素如果是Promise对象,只要改变状态,就会插入到返回的result的对应下标位置;如果是非Promise值,则会当作成功的Promise对象(加一层封装),最终所有的元素改变状态了,返回的Promise状态变为成功,result值变为这些元素result组成的数组。

result形如

[{status:'fulfiled',value:'OK'},{status:'rejected',reason:'ERR'}]

这样的形式