🤪🤪别看了,进来一起手写一个promise吧

1,023 阅读5分钟

前言

这一个手写专题已经有太多大佬写的很好了,为什么自己要在这里又重复写呢?

前不久在掘金上看到一位大佬这么说:学习就像爬山,每个人有每个人不同的上山方法,因此每个人也会领略到不一样的美景。

所以我把我看到的风景放在了在这里~

promise基础版

看看官方promsie的用例(也是接下来我们要率先实现的):

let p1 = new Promise((resolve, reject) => {
    resolve(1)
    reject(2)
    throw (3)
})
console.log(p1);

打印结果 image.png

前置知识

先补点基础知识:

  • 原生的promise我们一般都会用new来创建实例
    let promise = new Promise()
    
  • 原生的promise里面可以传入resolvereject两个参数
    let promise = new Promise((resolve, reject) => {})
    
  • promise有三种状态:分别是pendingfulfilledrejected
       初始的时候是`pending`
       `pending`可以转为`fulfilled`状态,但是不能逆转
       `pending`也可以转为`rejected`状态,但是也不能逆转
       这里`fulfilled``rejected`也不能互转
    

手写吧

定义初始结构

// 首先创建一个myPromise类
class myPromise {
// 类的`构造函数constructor`里面添加一个参数,这里就用
// executor来做形参,并且执行一下这个参数
    constructor(executor) {
       this.status = 'pending' // 定义初始状态
       executor();
   }
}

实现 resolve 和 reject

class myPromise {
    constructor(executor) {
        this.status = 'pending';
        this.value = null
        try {
            executor(this.resolve.bind(this), this.reject.bind(this))
        }
        catch (error) {
            this.reject(error)
        }

    }

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

    }

    reject(value) {
        if (this.status == 'pending') {
            this.status = 'reject'
            this.value = value
        }

    }
}

let p2 = new myPromise((resolve, reject) => {
    // resolve(1)
    // reject(2)
    throw ('1')
})

console.log(p2);
  • 那么在执行resolve()的时候就需要判断状态是否为 待定 pending,如果是 待定 pending的话就把状态改为 成功 fulfilled;在执行reject()的时候也是一样。
  • throw的效果和reject()一样,所以只需要在executor外层加上try catch 语句

this指向问题

可以看到这里加了bind

executor(this.resolve.bind(this), this.reject.bind(this))

如果我们不加bind

executor(this.resolve, this.reject)

因为这里this是在executor里面的,并且executor又是在constructor里执行的,所以this是指向constructor.

在这里我们就可以使用bind来绑定this,只需要在构造函数constructor中的this.resolvethis.reject后加上,.bind(this)就可以了

then方法

其实最难的就是实现then方法了。 先来看看then在Promise中是怎么使用的吧

  • 基础版
let p1 = new Promise((resolve, reject) => {
    resolve(1)
})
p1.then(res => console.log(res), err => console.log(err))
// 1
  • 升级版 then嵌套
let p1 = new Promise((resolve, reject) => {
    resolve(1)
})
p1.then(res => 2 * res, err => console.log(err))
.then(res => console.log(res), err => console.log(err)) 
// 2

前置知识

  • then 可以传入两个参数
    • 一个是 onFulfilled 表示 “当状态为成功时”
    • 另一个是 onRejected 表示 “当状态为拒绝时”
  • then 方法之间可以嵌套,所以说明了then方法会返回一个promise

手写吧

简单实现then嵌套

所以我们可以在then方法里返回一个promise对象,并且存储每次onFulfilled的返回值即可实现一个简单的then嵌套了。

class myPromise {
    constructor(executor) {
        this.status = 'pending'
        this.value = null;
        executor(this.resolve.bind(this), this.reject.bind(this))
    }

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

    reject(value) {
        if (this.status == 'pending') {
            this.status = 'reject'
            this.value = value
        }
    }
    then(onFulfilled, onRejected) {
        var thenPromise = new myPromise((resolve, reject) => {
            let x;
            if (this.status == 'fulfilled') {
                x = onFulfilled(this.value)
                resolve(x)
            } else if (this.status == 'reject') {
                x = onRejected(this.value)
                reject(x)
            }
        })

        return thenPromise
    }
}

测试用例

let p2 = new myPromise((resolve, reject) => {
    resolve(1)
    reject(10)
})

p2.then(res => 2 * res, err => err * 2)
.then(res => console.log(res), err => console.log(err))

// 2

then参数返回值的讨论

当然我们知道,onFulfilled的返回值不止这一种情况。

所以我们还得分情况讨论

  • 返回值是myPromise自身
let p2 = new myPromise((resolve, reject) => {
    resolve(1)
})

p2.then(res => p2, err => err * 2)

要作为错误抛出

  • 返回值是myPromise的实例
let p2 = new myPromise((resolve, reject) => {
    resolve(1)
})

p2.then(res => new myPromise((resolve, reject) => resolve(3 * res)))
  • 返回值是普通的变量
let p2 = new myPromise((resolve, reject) => {
    resolve(1)
})

p2.then(res => 2* res)

所以我们可以将代码改成如下形式

    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : val => val
        onRejected = typeof onRejected == 'function' ? onRejected : val => { throw val }

        var thenPromise = new myPromise((resolve, reject) => {
        // 用resolvePromise函数封装
            const resolvePromise = callback => {
                    try {
  // 拿到onFulfilled, onRejected的返回值,进行后续返回值的判断
                        const x = callback(this.value)
                        // 返回值为本身
                        if (x == thenPromise) {
                            throw new Error('不能放回自身..')
                        }
                        // 返回值为实例对象
                        if (x instanceof myPromise) {
                            x.then(resolve, reject) // 为了执行括号里的函数
                        }
                        else {
                        // 返回值为普通变量
                            resolve(x) // 改变状态和传参  为了执行函数结果
                        }
                    } catch (error) {
                        reject(error)
                        throw new Error(error)
                    }
            }


            if (this.status == 'fulfilled') {
                resolvePromise(onFulfilled)
            } else if (this.status == 'reject') {
                resolvePromise(onRejected)
            }

        })

        return thenPromise
    }

处理异步

我们最常见的功能已经实现了,现在来处理一些重要的细节。

  • myPromise中添加定时器
let p2 = new myPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    }, 1000)

})

p2.then(res => 2 * res)
  • then是微任务,所以要在同步代码之后执行
let p2 = new myPromise((resolve, reject) => {
    resolve(100)
}).then(res => 3 * res)
.then(res => console.log(res))

console.log(1);
// 1 
// 300 
  • 以及我们要对then里传入的参数进行一个类型的检测
onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : val => val
onRejected = typeof onRejected == 'function' ? onRejected : val => { throw val }

最终版promise.then代码就呼之欲出了~

class myPromise {
    constructor(executor) {
        this.status = 'pending';
        this.value = null;
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        try {
            executor(this.resolve.bind(this), this.reject.bind(this))
        }
        catch (error) {
            this.reject(error)
        }
    }

    resolve(value) {
        if (this.status == 'pending') {
            this.status = 'fulfilled'
            this.value = value
        }
        // 调用then里面的回调
        while (this.onFulfilledCallbacks.length) {
            this.onFulfilledCallbacks.shift()(this.value)
        }

    }

    reject(value) {
        if (this.status == 'pending') {
            this.status = 'reject'
            this.value = value
        }
        while (this.onRejectedCallbacks.length) {
            this.onRejectedCallbacks.shift()(this.value)
        }

    }

    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : val => val
        onRejected = typeof onRejected == 'function' ? onRejected : val => { throw val }

        var thenPromise = new myPromise((resolve, reject) => {
            const resolvePromise = callback => {
            // 简单地用宏任务来实现then的微任务功能,不太严谨,但功能可以实现
                setTimeout(() => {
                    try {
                        const x = callback(this.value) // onFulfilled, onRejected的返回值
                        if (x == thenPromise) {
                            throw new Error('不能放回自身..')
                        }
                        if (x instanceof myPromise) {
                            x.then(resolve, reject)
                        }
                        else {
                            resolve(x) // 改变状态和传参
                        }
                    } catch (error) {
                        reject(error)
                        throw new Error(error)
                    }
                })

            }

            if (this.status == 'fulfilled') {
                resolvePromise(onFulfilled)  // 判断onFulfilled返回值判断
            }

            else if (this.status == 'reject') {
                resolvePromise(onRejected)
            }
            else if (this.status == 'pending') {
               // 如果myPromise中存在定时器,此时this.status还是pending状态
               // 应该放入回调函数中储存起来
               this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled))
                this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected))
            }

        })
        return thenPromise

    }
}

参考文档

本文正在参加「金石计划 . 瓜分6万现金大奖」

手把手一行一行代码教你“手写Promise“,完美通过 Promises/A+ 官方872个测试用例 - 掘金 (juejin.cn)