简单实现「Promise」,从入门到手写

前言

Promise 已经成为前端开发必备技能,它主要解决了异步编程时大量嵌套回调函数,导致的回调低于问题,本篇我将以学习笔记的形式,和大家一起来深入学习它。内容主要包括基本用法手写两部分,适合刚入门的新手和想深入了解原理的小伙伴。

认识 Promise

  • CommonJS 社区首先提出了 Promise 规范,在 ES2015 中被标准化,成为语言规范
  • Promise 是一个对象,用来表示一个异步任务结束之后,是成功了还是失败了,任何一个 Promise 对象的初始状态都为 Pending,当任务成功后,状态改为 Fulfilled,然后执行成功任务的回调 onFufilled;当任务失败后,状态改为 Rejected,然后执行失败任务的回调 onRejected
  • 状态只能从 Pending 变为 Fulfilled 或者从 Pending 变成 Rejected,只有这两种变化,而且改变之后将不可能再对其进行修改,就像我承诺,今天晚上要请你吃饭,如果晚上请了,就是 Fulfilled,如果晚上没请,就是 Rejected,假如我说今天要加班,改成明天吧,那也是 Rejected,因为明天的承诺是明天的,今天这个承诺已经有结果了,无法改变了。

Promise 基本用法

第一步:创建实例

PromiseES2015 中新增的一个全局对象,通过 new 创建一个 Promise 实例,传入一个 executor 函数,这个函数会立即开始执行,主要的业务流程都在 executor 函数中

const promise = new Promise((resolve,reject)=>{
    if(/*异步操作成功*/){
        resolve('执行成功了')
    }else{
        reject(new Error('执行失败了'))
    }
})
复制代码

resolvereject两个函数作为参数传递给 executor 函数

  • 调用 resolve 函数时,将我们的 Promise 状态修改为 Fulfilled,并将异步操作成功后的结果作为参数传递出去
  • 调用 reject 函数时,将我们的 Promise 状态修改为 Rejected,并将异步操作失败的原因作为参数传递出去
  • 二者只能调用其一,要么成功,要么失败
  • 需要注意的是,Promise 是用来管理异步的,但它本身不是异步的,executor 函数会立即执行,我们往往会在 executor 中编写异步操作

第二步:调用 then 方法

Promise 实例创建好之后,使用它的 then 方法来指定 onFulfilled 或者 onRejected 的回调函数,第一个参数是 onFulfilled 回调,第二个参数是 onRejected 回调,其中 onRejected 可以省略。

promise.then( value =>{
    console.log('resolved',value)
}, error =>{ // onRejected 回调可以省略
    console.log('rejected',error)
})
复制代码

执行时序

即使我们没有执行任何异步操作,promise.then 中的回调函数,也是在 JS 执行完同步任务之后才去执行:

const promise = new Promise((resolve,reject)=>{
  console.log(1)
  resolve(100)
  console.log(2)
})
promise.then( value =>{
    console.log('resolved',value)
}, error =>{
    console.log('rejected',error)
})

console.log('3')

/*输出结果*/
// 1
// 2
// 3
// resolved 100
复制代码

new Promise 时先执行 executor 函数,打印出 1 和 2,因为 resolve 是微任务,先不执行它,继续往下执行同步任务,执行 promise.then,将成功回调和失败回调都存储到起来(存储到 Promie 实例的变量中,后面手写 Promise 的内容会讲到),此时先不执行他们,然后打印出 3,此时同步任务执行完毕,开始执行微任务,调用 then 方法的成功回调,打印出 resolved 100

然后我们再加入定时器来看一下:

const promise = new Promise((resolve,reject)=>{
  console.log(1)
  resolve(100)
  console.log(2)
})

// 在此加入定时器,执行顺序是怎样的呢?
setTimeout(()=>{
    console.log('setTimeout')
},0)

promise.then( value =>{
    console.log('resolved',value)
}, error =>{
    console.log('rejected',error)
})

console.log('3')

/*输出结果*/
// 1
// 2
// 3
// resolved 100
// setTimeout
复制代码

setTimeout 的时间为 0,会立即进入回调队列中排队,等待下一轮的执行。这里有个误区,因为 setTimeoutPromise 先执行,我们会以为 setTimeout 会先进入队列中,就先执行,但实际并不是这样。

因为 Promise 属于微任务,setTimeout 属于宏任务,当前同步代码执行完毕后,如果有微任务,就顺带把微任务执行了,如果有宏任务,则等到下一个队列中去执行。关于宏任务和微任务的概念与区别,具体可以参考微任务、宏任务与Event-Loop

微任务是为了提高整体的响应能力,在 JS 中,大部分异步任务都是宏任务,只有 PromiseMutationObserver 和 node 中的 process.nextTick 会作为微任务执行。

常见的错误

Promise 的本质还是回调,当异步任务结束后,通过 then 方法执行回调,有的同学不自然就会嵌套回调,这是因为对 Promise 用法不清晰,不知道 Promise 链式调用的特点。

promiseA.then(function(value1){
    promiseB.then(function(value2){
        promiseC.then(function(value3){
            /*回调地狱*/
            ......
        })
    })
})
复制代码

Promise 的链式调用

then 方法在执行完成后返回一个新的 Promise 对象,因此可以使用链式调用避免回调地狱,使得代码扁平化。

Promise 的链式调用与传统的链式调用有所不同:

  • 传统链式调用是在函数中返回 this
  • Promise 链式调用是在 then 中返回一个新的 Promise 对象
const promise1 = new Promise((resolve,reject)=>{
    resolve(100)
})
const promise2 = promise1.then((value)=>{
    console.log(value)
})
const promsie3 = promise2.then((value)=>{
    console.log(value)
})

console.log(promise1 === promise2) // false 证明返回的是新 Promise 对象
console.log(promise2 === promise3) // false
复制代码

因为 then 返回的是一个新 Promise,所以当前调用的 then,是在给上一个 then 返回的 Promise 对象,添加状态改变后的回调:

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

/*链式调用*/
promise
.then((value)=>{
    console.log('11111')
})
.then((value)=>{
    console.log('22222')
})
.then((value)=>{
    console.log('33333')
})
复制代码

我们可以在 then 方法中手动返回新的 Promise,也可以返回一个普通值:

promise
.then((value)=>{
    // 手动返回新的 Promise
    return new Promise((resolve,reject)=>{
        resolve(800)
    })
})
.then((value)=>{
    console.log(value) // 800
    // 手动返回普通值
    return 'hello promise'
})
.then((value)=>{
    console.log(value) // hello promise
})
.then((value)=>{
    console.log(value) // undefined   上一个 then 没有返回值
})
复制代码

异常处理

promise
.then(function(value){
    console.log(1)
})
.then(function(value){
    throw Error('error')
})
.then(function(value){
    console.log(2)
})
.catch(function(error){
    console.log('onRejected',error)
})
// 输出
// 1
// error
复制代码
  • 在链式调用的最后使用 catch 捕获异常,指定失败的回调
  • 它捕获到的是整个链式调用中所有的异常,有点类似于 “冒泡”,一直向后传递,直到被 catch 捕获
  • 通过这种方式,我们不必在每个 then 中写 onRejected 回调,也不必每个 then 后面写 catch,这使我们的代码更优雅

静态方法

Promise.resolve( )

如果接收的是普通值,把普通值作为结果,返回一个 Promise

// 返回状态为 Fulfilled 的 Promise 对象
Promise.resolve('hello world')
.then(value=>{
    console.log(value) // hello world
})
// 等价于
new Promise((resolve,reject)=>{
    resolve('hello world')
})
复制代码

如果接收的是另一个 Promise 对象,则原样返回

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

console.log(promise === promise2) // true
复制代码

Promise.reject( )

返回一个失败的 Promise,无论传入什么参数,都将作为失败原因

cosnt promise = Promise.reject('hello world')
复制代码

Promise.all( )

将多个 Promise 实例组合成一个新的 Promise 实例,它的成功和失败返回值不同,成功时返回的是一个数组,包含每个 Promise 的执行结果,失败时返回的是最先 reject 的值。

  • Promise.all 当所有 Promise 对象都成功了,才会执行成功回调,只要有一个失败了,就会执行失败回调
const promise = Promise.all([
    Promise.resolve({code:200,data:[1,2]}),
    Promise.resolve({code:200,data:[3,4]}),
    Promise.resolve({code:200,data:[5,6]}),
])

promise.then(function(values){
    console.log(values) // 此时拿到的 values 是数组,包含每个 promise 异步任务的执行结果
})
复制代码

比如我们要同时请求多个接口,当所有数据都返回时再执行下一步,这时就可以使用Promise.all 方法

注意:Promise.all 得到的成功结果数组,与调用 Promise.all 时传入的实例顺序相同,也就是上面代码中,请求接口1、请求接口2、请求接口3的顺序,即使请求接口1最后才获取到结果,也是放在最前面。

Promise.allSettled( )

有时我们不关心异步操作的结果,只关心这些操作有没有执行完,于是在 ES2020 中引入了 Promise.allSettled 方法

Promise.allSettled([
    Promise.resolve({code:200,data:[1,2]}),
    Promise.reject({code:500,errMsg:'服务器异常'}),
    Promise.resolve({code:200,data:[3,4]})
])
.then(function(values){
    console.log(values)
})
复制代码

Promise.allSettledPromise.all 很相似,唯一不同的是,Promise.allSettled 会拿到每一个 Promise 的状态,无论它是成功或者失败。

Promie.race( )

同样是支持多个 Promise 调用,但与 Promise.all 有所不同,race 是赛跑的意思,Promise.race([p1,p2,p3]) 中哪个执行最快, 就返回哪个结果,无论它是成功还是失败

  • Promise.all 等待所有任务完成后,新返回的 Promise 才会完成
  • Promise.race 只要有一个任务成功,新返回的 Promise 就会成功,只要有一个 Promise 失败,返回的 Promise 就会失败

Promise.finally( )

ES9 新怎了 finally 方法,无论结果是 Fulfilled 还是 Rejected,都会执行 finally 指定的回调函数,这样就避免了我们在 thencatch 中各写一次操作的情况

// 请求接口时让页面转圈圈
this.loading = true

axios.get('http://juejin.cn/api')
    .then(res => {
        // this.loading = false
    })
    .catch(err => {
        // this.loading = false
    })
    .finally(() => {
        this.loading = false
    })
复制代码

手写 Promise

我们不能直接罗列完整的 Promise 代码,那样看不出思路,我们要从无到有、渐进式地完成这项工作。所以我会在每一步的关键位置写上注释,通过前后代码对比的方式来学习,看似代码很多,但重点在于增量代码。

一个最基本的 Promise

要想手写 Promise,得先对它的使用方式和特点做下总结:

  • Promise 通过 new 出来,那一定是个构造函数或者类,我们就用类的方式来实现
  • 接收一个执行器函数 executor,并且能够立即执行
  • 执行器函数接收 resolvereject,它们用于改变 Promise 的状态
  • Promise 有三种状态:PenddingFulfilledRejected,且一旦确定了就不能改变
  • 通过 then 方法判断状态,如果是成功则执行 onFulfilled,如果是失败则执行 onRejected
// 定义三个状态常量
const PENDDING = 'PENDDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class MyPromise {
    // 接收一个执行器函数 executor
    constructor(executor) {
        // 立即执行 executor,传入 resolve 和 reject 函数
        executor(this.resolve, this.reject)
    }
    // 每个 Promise 实例都有一个属于自己的状态,初始为 PENDDING
    status = PENDDING
    
    // 缓存成功结果和失败原因
    value = undefined
    reason = undefined

    resolve = value => {
        /**
         * 1.业务代码的异步函数成功后,调用 resolve 并传入成功值 value
         * 2.resolve 的作用是将状态改为 FULFILLED,并将 value 保存起来
         * 3.为什么是箭头函数:我们是在 executor 中直接调用 resolve 的,如果是普通函数,this 会指向 window,我们需要 this 指向 Promise 实例
         * 4.如果状态不是 PENDDING,则不允许修改
         */
        if (this.status !== PENDDING) return

        this.status = FULFILLED

        // 保存成功后的值,将来在 then 方法的成功回调中使用
        this.value = value
    }

    reject = reason => {
        // 与 resolve 类似,reject 用于处理异步任务失败的情况
        if (this.status !== PENDDING) return

        this.status = REJECTED

        // 保存失败后的原因,将来在 then 方法的失败回调中使用
        this.reason = reason
    }

    /**
     * 1.每个 Promise 实例都有 then 方法,所以将 then 定义在类中
     * 2.then 方法接收两个参数,成功回调和失败回调
     * 3.根据状态判断调用哪个回调,并传入相应的值
     */
    then(onFulfilled, onRejected) {
        if (this.status === FULFILLED) {
            onFulfilled(this.value)
        } else if (this.status === REJECTED) {
            onRejected(this.reason)
        }
    }

}
// 最后别忘了导出
module.exports = MyPromise
复制代码

测试一下是否可用

const MyPromise = require('./promise.js')

let promise = new MyPromise((resolve, reject) => {
    resolve('成功')
})

promise.then(value => {
    console.log(value)
}, reason => {
    console.log(reason)
})
复制代码

处理异步情况

接下来处理下业务中的异步情况,我们在业务代码中加入定时器

const MyPromise = require('./promise.js')

let promise = new MyPromise((resolve, reject) => {
    // 异步情况
    setTimeout(()=>{
        resolve('成功');
    },2000)
})

// 主线程不会等待 setTimeout,而是先执行到这里 
promise.then(value => {
    console.log(value)
}, reason => {
    console.log(reason)
})
复制代码

主线程不会等待 setTimeout,往下执行到 then,但此时还没有执行 resolve 或者 reject,也就意味着 Promise 的状态还是 PENDDING,所以要在 then 中加一个判断:如果是 PENDDING 状态,则先将 onFulfilledonRejected 回调函数存储起来,等到异步任务执行完毕,开始执行 resolve 或者 reject 的时候再去调用回调函数

const PENDDING = 'PENDDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class MyPromise {
    constructor(executor) {
        executor(this.resolve, this.reject)
    }
    
    status = PENDDING
    
    value = undefined
    reason = undefined
    
    // 缓存成功回调与失败回调
    onFulfilled = undefined
    onRejected = undefined

    resolve = value => {
        if (this.status !== PENDDING) return

        this.status = FULFILLED

        this.value = value

        /**
         * 针对异步情况,判断成功回调是否存在,如果存在就调用
         */
        this.onFulfilled && this.onFulfilled(this.value)
    }

    reject = reason => {
        if (this.status !== PENDDING) return

        this.status = REJECTED

        this.reason = reason

        /**
         * 针对异步情况,判断失败回调是否存在,如果存在就调用
         */
        this.onRejected && this.onRejected(this.reason)
    }

    then(onFulfilled, onRejected) {
        if (this.status === FULFILLED) {
            onFulfilled(this.value)
        } else if (this.status === REJECTED) {
            onRejected(this.reason)
        } else {
            /**
             * 如果是 PENDDING 状态,表明此时是异步情况
             * 但我们还不知道将来是成功还是失败,所以把它们都先存储起来
             * */
            this.onFulfilled = onFulfilled
            this.onRejected = onRejected
        }
    }
}

module.exports = MyPromise
复制代码

处理多个回调情况

then 方法会被调用多次,每次都会传入成功或失败的回调函数,这些回调都是要执行的,所以要改进一下,将这些函数存储到数组中,在 resolvereject 中依次调用

const PENDDING = 'PENDDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class MyPromise {
    constructor(executor) {
        executor(this.resolve, this.reject)
    }

    status = PENDDING

    value = undefined
    reason = undefined


    // 改成用数组存储这些回调
    // onFulfilled = undefined
    // onRejected = undefined
    onFulfilled = []
    onRejected = []

    resolve = value => {
        if (this.status !== PENDDING) return

        this.status = FULFILLED

        this.value = value

        // 由只调用一次,改为遍历数组,调用所有回调函数
        // this.onFulfilled && this.onFulfilled(this.value)
        while (this.onFulfilled.length) {
            this.onFulfilled.shift()(this.value)
        }

    }

    reject = reason => {
        if (this.status !== PENDDING) return

        this.status = REJECTED

        this.reason = reason

        // 由只调用一次,改为遍历数组,调用所有回调函数
        // this.onRejected && this.onRejected(this.reason)
        while (this.onRejected.length) {
            this.onRejected.shift()(this.reason)
        }
    }

    then(onFulfilled, onRejected) {
        if (this.status === FULFILLED) {
            onFulfilled(this.value)
        } else if (this.status === REJECTED) {
            onRejected(this.reason)
        } else {
            // 回调可能为多个,全都存储起来
            // this.onFulfilled = onFulfilled
            // this.onRejected = onRejected
            this.onFulfilled.push(onFulfilled)
            this.onRejected.push(onRejected)
        }
    }
}

module.exports = MyPromise
复制代码

then 方法的链式调用

  • then 方法可以链式调用,每次返回的都是 Promise 对象
  • 上一个 then 的返回值,传递给当前 then 的回调函数
const PENDDING = 'PENDDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class MyPromise {
    constructor(executor) {
        executor(this.resolve, this.reject)
    }

    status = PENDDING

    value = undefined
    reason = undefined

    onFulfilled = []
    onRejected = []

    resolve = value => {
        if (this.status !== PENDDING) return

        this.status = FULFILLED

        this.value = value

        while (this.onFulfilled.length) {
            this.onFulfilled.shift()(this.value)
        }

    }

    reject = reason => {
        if (this.status !== PENDDING) return

        this.status = REJECTED

        this.reason = reason

        while (this.onRejected.length) {
            this.onRejected.shift()(this.reason)
        }
    }

    then(onFulfilled, onRejected) {
        /**
         * then 方法需要返回一个新的 Promise 实例,实现链式调用
         */
        const newPromise = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                // 拿到当前 then 的成功回调函数返回值
                let current = onFulfilled(this.value)

                /**
                 * current 可能是 promise 或者普通值,针对不同情况要做不同处理,
                 * 如果是普通值,直接调用 resolve
                 * 如果是 promise,查看 promise 的返回结果,再决定调用 resolve 还是 reject 
                 * 我们把处理过程封装成一个函数,方便给 onRejected 同样使用,
                 * 同时还要对比一下 newPromise 和 current,防止他们是同一个 promise,也就是循环调用的问题
                 * */
                resolvePromise(newPromise, current, resolve, reject)
            } else if (this.status === REJECTED) {
                let current = onRejected(this.reason)
                resolvePromise(current, resolve, reject)
            } else {
                this.onFulfilled.push(onFulfilled)
                this.onRejected.push(onRejected)
            }
        })

        return newPromise
    }
}

function resolvePromise(newPromise, current, resolve, reject) {
    if (current === newPromise) {
        return reject(new TypeError('不能循环调用'))
    }
    if (current instanceof MyPromise) {
        current.then(value => {
            resolve(value)
        }, reason => {
            reject(reason)
        })
    } else {
        resolve(current)
    }
}
module.exports = MyPromise
复制代码

细心的同学会发现,newPromise 是在 new Promise 执行完成之后返回的,我们在调用 resolvePromise 时传入的 newPromise 此时还没有获取到,所以我们通过 setTimeout 将其转换成异步代码

then(onFulfilled, onRejected) {

    const newPromise = new MyPromise((resolve, reject) => {
        if (this.status === FULFILLED) {
            // 转为异步代码,是为了获取到 newPromise
            setTimeout(() => {
                let current = onFulfilled(this.value)
                resolvePromise(newPromise, current, resolve, reject)
            }, 0);
        } else if (this.status === REJECTED) {
            setTimeout(() => {
                let current = onRejected(this.reason)
                resolvePromise(current, resolve, reject)
            }, 0);
        } else {
            this.onFulfilled.push(onFulfilled)
            this.onRejected.push(onRejected)
        }
    })

    return newPromise
}
复制代码

错误处理机制

  • 如果执行器函数出错了,应该在 constructor 中捕获出来并抛出
  • 如果 onFulfilled 或者 onRejected 出错了,也应该捕获并抛出
const PENDDING = 'PENDDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class MyPromise {
    constructor(executor) {
        // 捕获执行器函数的错误
        try {
            executor(this.resolve, this.reject)
        } catch (err) {
            this.reject(err)
        }
    }

    status = PENDDING

    value = undefined
    reason = undefined

    onFulfilled = []
    onRejected = []

    resolve = value => {
        if (this.status !== PENDDING) return

        this.status = FULFILLED

        this.value = value

        while (this.onFulfilled.length) {
            // 不需要传值了
            // this.onFulfilled.shift()(this.value)
            this.onFulfilled.shift()()
        }

    }

    reject = reason => {
        if (this.status !== PENDDING) return

        this.status = REJECTED

        this.reason = reason

        while (this.onRejected.length) {
            // 不需要传值了
            // this.onRejected.shift()(this.reason)
            this.onRejected.shift()()
        }
    }

    then(onFulfilled, onRejected) {

        const newPromise = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    // 捕获 onFulfilled 的错误
                    try {
                        let current = onFulfilled(this.value)
                        resolvePromise(newPromise, current, resolve, reject)
                    } catch (err) {
                        reject(err)
                    }
                }, 0);
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    // 捕获 onRejected 的错误
                    try {
                        let current = onRejected(this.reason)
                        resolvePromise(current, resolve, reject)
                    } catch (err) {
                        reject(err)
                    }
                }, 0);
            } else {
                // 捕获 onFulfilled 和 onRejected 的错误
                // this.onFulfilled.push(onFulfilled)
                // this.onRejected.push(onRejected)
                this.onFulfilled.push(() => {
                    setTimeout(() => {
                        // 捕获 onFulfilled 的错误
                        try {
                            let current = onFulfilled(this.value)
                            resolvePromise(newPromise, current, resolve, reject)
                        } catch (err) {
                            reject(err)
                        }
                    }, 0);
                })
                this.onRejected.push(() => {
                    setTimeout(() => {
                        // 捕获 onRejected 的错误
                        try {
                            let current = onRejected(this.reason)
                            resolvePromise(current, resolve, reject)
                        } catch (err) {
                            reject(err)
                        }
                    }, 0);
                })
            }
        })

        return newPromise
    }
}

function resolvePromise(newPromise, current, resolve, reject) {
    if (current === newPromise) {
        return reject(new TypeError('不能循环调用'))
    }
    if (current instanceof MyPromise) {
        current.then(value => {
            resolve(value)
        }, reason => {
            reject(reason)
        })
    } else {
        resolve(current)
    }
}
module.exports = MyPromise
复制代码

实现 Promise.all( )

  • Promise.all 是静态方法,通过 static 声明
  • 接收一个数组,每一项都是一个 Promise 实例或者普通值,遍历这个数组,当所有 Promise 执行完毕之后,再返回结果
// Promise.all 是静态方法,通过 static 声明
static all(array) {
    let result = [];
    //计数器
    let index = 0;
    return new MyPromise((resolve, reject) => {
        function addData(key, value) {
            result[key] = value;
            index++;
            /**
             * 因为 for 循环中会存在异步函数,当 for 循环执行完了,异步函数还没有返回结果,此时执行 resolve 会出错
             * 所以我们等循环完成之后再执行 resolve
             * */
            if (index === array.length) {
                resolve(result);
            }
        }
        for (let i = 0; i < array.length; i++) {
            let data = array[i];
            if (data instanceof MyPromise) {
                // data 是 promise 对象的情况
                data.then(value => addData(i, value), reason => reject(reason))
            } else {
                // data 是普通值的情况
                addData(i, data)
            }
        }
    })
}
复制代码

实现 Promise.resolve( ) 和 Promise.reject( )

这两个比较简单,直接撸!

static resolve(value) {
    return new MyPromise((resolve, reject) => {
        resolve(value)
    })
}

static reject(value) {
    return new MyPromise((resolve, reject) => {
        reject(err);
    })
}
复制代码

如果 finally 的回调函数返回的是一个异步的 promise 对象

实现 catch

如果我们调用 then 方法的时候,没有传递失败回调,那么错误最终会被 catch 捕获

catch (onRejected) {
    return this.then(undefined, onRejected)
}
复制代码

完整的代码

const PENDDING = 'PENDDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class MyPromise {
    constructor(executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (err) {
            this.reject(err)
        }
    }

    status = PENDDING

    value = undefined
    reason = undefined

    onFulfilled = []
    onRejected = []

    resolve = value => {
        if (this.status !== PENDDING) return

        this.status = FULFILLED

        this.value = value

        while (this.onFulfilled.length) {
            this.onFulfilled.shift()()
        }

    }

    reject = reason => {
        if (this.status !== PENDDING) return

        this.status = REJECTED

        this.reason = reason

        while (this.onRejected.length) {
            this.onRejected.shift()()
        }
    }

    then(onFulfilled, onRejected) {

        const newPromise = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    try {
                        let current = onFulfilled(this.value)
                        resolvePromise(newPromise, current, resolve, reject)
                    } catch (err) {
                        reject(err)
                    }
                }, 0);
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let current = onRejected(this.reason)
                        resolvePromise(current, resolve, reject)
                    } catch (err) {
                        reject(err)
                    }
                }, 0);
            } else {
                this.onFulfilled.push(() => {
                    setTimeout(() => {
                        try {
                            let current = onFulfilled(this.value)
                            resolvePromise(newPromise, current, resolve, reject)
                        } catch (err) {
                            reject(err)
                        }
                    }, 0);
                })
                this.onRejected.push(() => {
                    setTimeout(() => {
                        try {
                            let current = onRejected(this.reason)
                            resolvePromise(current, resolve, reject)
                        } catch (err) {
                            reject(err)
                        }
                    }, 0);
                })
            }
        })

        return newPromise
    }

    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 data = array[i];
                if (data instanceof MyPromise) {
                    data.then(value => addData(i, value), reason => reject(reason))
                } else {
                    addData(i, data)
                }
            }
        })
    }

    static resolve(value) {
        return new MyPromise((resolve, reject) => {
            resolve(value)
        })
    }

    static reject(value) {
        return new MyPromise((resolve, reject) => {
            reject(err);
        })
    }

    catch (onRejected) {
        return this.then(undefined, onRejected)
    }
}

function resolvePromise(newPromise, current, resolve, reject) {
    if (current === newPromise) {
        return reject(new TypeError('不能循环调用'))
    }
    if (current instanceof MyPromise) {
        current.then(value => {
            resolve(value)
        }, reason => {
            reject(reason)
        })
    } else {
        resolve(current)
    }
}

module.exports = MyPromise
复制代码

写在最后

写到这里基本就差不多了,虽然代码中还存在可以优化的点,可以继续打磨,但通过这篇文章的整理,我对 Promise 又加深了一次印象,学习就是不断遗忘的过程,需要我们不断巩固加深记忆,加油伙伴们!

参考资料

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享