带你手写Promise身上的几个方法,拷打面试官

前言

Promise的手写是面试中一个常考的考点。希望我的文章能够帮到大家,在被问到Promise时能够露出一个自信的微笑。

constructor

首先来写一个Promise的构造器,这有帮助于我们了解Promise的内部结构和他各个方法的底层原理。

js 复制代码
class myPromise{
constructor(executor){
    this.state = 'pending' // 记录promise的状态,pending...等
    this.value = undefined // resolve传递的值
    this.reason = undefined // reject传递的值
    this.onFullfilledCallback = [] // resovle的回调函数
    this.onRejectedCallback = [] // rejected的回调函数
    const resolve = (value)=>{
        if(this.state === 'pending'){
            this.state = 'fulfilled'// 判断当前对象状态
            this.value = value
            this.onFullfilledCallback.forEach(callback=>callback(value))
        }
    }
    const reject = (reason)=>{
        if(this.state === 'pending'){
            this.state = 'rejected'// 判断当前对象状态
            this.reason = reason
            this.onRejectedCallback.forEach(callback=>callback(reason))
        }
    }
    executor(resolve, reject) // 执行器函数
}
}

这种手写的话我们都要想一想我们平时使用promise有哪些特点。首先我们知道

  • promise有三种状态,pending,fulfilled,reject
  • promise状态一旦修改无法二次修改(为了确认异步操作的正确性)
  • resolve和reject可以接受一个回调函数 基于这些我们就写出了以上这些代码。

then

js 复制代码
then(onFulfilled,onRejected){
// 判断是否传递了一个回调参数
  onFullfilled = typeof onFullfilled === 'function' ? onFullfilled : value => value
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
        const newPromise = new myPromise((resolve, reject) => {
            // 考虑onFullfilled,onRejected
            if (this.state === 'fulfilled') {// then前面的对象状态已经变更
                setTimeout(() => {// 模拟异步但是模拟不了微任务
                    try {
                        const result = onFullfilled(this.value)
                        resolve(result)
                    } catch (e) {
                        reject(e)
                    }
                });
            }
            if (this.state === 'rejected') {
                setTimeout(() => {// 模拟异步但是模拟不了微任务
                    try {
                        const result = onRejected(this.reason)
                        resolve(result)
                    } catch (e) {
                        reject(e)
                    }
                });
            }
            if (this.state === 'pending') {
                this.onFullfilledCallback.push((value) => {
                    setTimeout(() => {
                        try {
                            const result = onFullfilled(value)
                            resolve(result)
                        } catch (error) {
                            reject(error)
                        }
                    });
                })
                this.onRejectedCallback.push((reason) => {
                    setTimeout(() => {
                        try {
                            const result = onRejected(reason)
                            resolve(result)
                        } catch (error) {
                            reject(error)
                        }
                    });
                })
            }
        })
        return newPromise
}
  • then中可以接受两个参数,当上一个promise对象为fulfilled时调用第一个回调函数,rejected调用第二个回调函数
  • then需要返回一个新的promise对象并且返回和上一个promise对象resolve或者rejected对应的值

catch

kotlin 复制代码
catch(onRejected) {
        this.then(null, onRejected)
    }

race

js 复制代码
 // race 只找最快的,无论是resolve和rejected都只取最快的一个
    static race(promises) {
        return new myPromise((resolve, reject) => {
            promises.forEach(promise => {
                // promise的状态是不是fulfilled
                promise.then((value) => {
                    resolve(value)
                }, (reason) => {
                    reject(reason)
                })
            })
        })
    }
  • race会返回一个新的promise对象
  • race接受一个数组为参数,数组每一个元素都要是一个promise对象
  • race会取数组中速度最快的一个为结果并返回,无论是promise还是rejected

all

js 复制代码
// all 当所有的都是resolve就会以数组形式返回所有的值,有一个reject直接reject
    static all(promises) {
        return new myPromise((resolve, reject) => {
            let count = 0
            let arr = []
            // 判断数组中所有的promise状态是否都为fulfilled
            promises.forEach((promise, i) => {
                promise.then((value) => {
                    count++
                    arr[i] = value
                    if (count === promise.length) {
                        resolve(arr)
                    }
                }, (reason) => {
                    reject(reason)
                })
            })
        })
    }
  • all也会返回一个新的promise对象且也接受一个元素全为promise的数组为参数
  • 当数组中所有元素的状态都为resolve时就返回所有的数组元素,有一个为rejected就返回那一个rejected的对象,无论哪种情况都要给返回的peomise对象设置对应的状态。

any

js 复制代码
// 只要有一个能resolve就直接resolve,全部reject才reject
    static any(promises) {
        return new myPromise((resolve, reject) => {
            let count = 0
            let arr = []
            promises.forEach((promise, i) => {
                promise.then((value) => {
                    resolve(value)
                }, (reason) => {
                    count++
                    arr[i] = reason
                    if (count === promises.length) {
                        reject(new AggregateError(arr, 'All promise were rejected'))
                    }
                })
            })
        })
    }
  • any和all相反,只要有一个元素状态为resolve就直接返回那一个resolve对象并用一个新的promise对象包裹且设置对应的状态
  • 全为rejected则返回整个数组也是promise对象包裹且状态设置为rejected

finally

javascript 复制代码
 // finally在前一个promise对象结束时,无论是fulfilled还是rejected都会调用内部回调
    finally(callback) {
        return this.then(() => {
            (value) => { return Promise.resolve(callback()).then(() => value) }
        }, () => {
            (reason) => { return Promise.resolve(callback()).then(() => reason) }
        })
    }
  • finally对于上一个promise对象无论他的值为什么都要调用其回调函数
  • 并且要返回其对应的resolve或者rejected对应的值

allSettled

js 复制代码
 static allSettled(promises) {
        let arr = []
        let count = 0
        return new myPromise((resolve, reject) => {
            promises.forEach((promise, i) => {
                promise.then((value) => {
                    arr[i] = { status: 'fulfilled', vlaue: value }
                }, (reason) => {
                    arr[i] = { status: 'rejected', reason: reason }
                }).finally(() => {
                    count++
                    // 所有promise状态都变更
                    if (count === promises.length) {
                        resolve(arr)
                    }
                })
            })
        })
    }
  • allSettled接受一个数组为参数
  • 当数组中所有元素的状态都发生变更时才会调用其内部的回调并返回一个新的promise对象
  • 返回的对象只要发生状态变更一定是fulfilled

尾声

Promise主要的几个手写方法就是这样,对于这种手写题我们一定要思考我们平时使用时有哪些特征,并根据这些特征一步步编写我们的代码,最后就能完全成型。

相关推荐
蓝天白云下遛狗3 分钟前
goole chrome变更默认搜索引擎为百度
前端·chrome
come1123427 分钟前
Vue 响应式数据传递:ref、reactive 与 Provide/Inject 完全指南
前端·javascript·vue.js
前端风云志1 小时前
TypeScript结构化类型初探
javascript
musk12121 小时前
electron 打包太大 试试 tauri , tauri 安装打包demo
前端·electron·tauri
翻滚吧键盘2 小时前
js代码09
开发语言·javascript·ecmascript
万少2 小时前
第五款 HarmonyOS 上架作品 奇趣故事匣 来了
前端·harmonyos·客户端
OpenGL2 小时前
Android targetSdkVersion升级至35(Android15)相关问题
前端
rzl022 小时前
java web5(黑马)
java·开发语言·前端
Amy.Wang2 小时前
前端如何实现电子签名
前端·javascript·html5
海天胜景2 小时前
vue3 el-table 行筛选 设置为单选
javascript·vue.js·elementui