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

相关推荐
莹雨潇潇几秒前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr9 分钟前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho1 小时前
【TypeScript】知识点梳理(三)
前端·typescript
安冬的码畜日常2 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js
太阳花ˉ2 小时前
html+css+js实现step进度条效果
javascript·css·html
小白学习日记2 小时前
【复习】HTML常用标签<table>
前端·html
john_hjy3 小时前
11. 异步编程
运维·服务器·javascript
风清扬_jd3 小时前
Chromium 中JavaScript Fetch API接口c++代码实现(二)
javascript·c++·chrome
丁总学Java3 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js