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

相关推荐
秦jh_10 分钟前
【Linux】多线程(概念,控制)
linux·运维·前端
蜗牛快跑21323 分钟前
面向对象编程 vs 函数式编程
前端·函数式编程·面向对象编程
Dread_lxy24 分钟前
vue 依赖注入(Provide、Inject )和混入(mixins)
前端·javascript·vue.js
涔溪1 小时前
Ecmascript(ES)标准
前端·elasticsearch·ecmascript
用户3157476081351 小时前
成为程序员的必经之路” Git “,你学会了吗?
面试·github·全栈
榴莲千丞1 小时前
第8章利用CSS制作导航菜单
前端·css
奔跑草-1 小时前
【前端】深入浅出 - TypeScript 的详细讲解
前端·javascript·react.js·typescript
羡与2 小时前
echarts-gl 3D柱状图配置
前端·javascript·echarts
guokanglun2 小时前
CSS样式实现3D效果
前端·css·3d
咔咔库奇2 小时前
ES6进阶知识一
前端·ecmascript·es6