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

相关推荐
RisunJan14 分钟前
Linux命令-named-checkzone
linux·前端
小陈工16 分钟前
Python Web开发入门(十):数据库迁移与版本管理——让数据库变更可控可回滚
前端·数据库·人工智能·python·sql·云原生·架构
吹晚风吧33 分钟前
解决vite打包,base配置前缀,nginx的dist包找不到资源
服务器·前端·nginx
Redemption1 小时前
嵌软面试每日一阅----Linux驱动之字符设备驱动
linux·面试·职场和发展
东离与糖宝1 小时前
HashMap从入门到源码:Java7/8/21区别+面试陷阱+高频追问合集
java·人工智能·面试
weixin199701080161 小时前
《施耐德商品详情页前端性能优化实战》
前端·性能优化
不想上班只想要钱1 小时前
模板里 item.xxx 报错 ,报 item的类型为未知
前端·vue
妖萌妹儿2 小时前
postman怎么做参数化批量测试,测试不同输入组合
开发语言·javascript·postman
阿琳a_2 小时前
在github上部署个人的vitepress文档网站
前端·vue.js·github·网站搭建·cesium
酉鬼女又兒2 小时前
零基础快速入门前端ES6 核心特性详解与蓝桥杯 Web 考点实践(可用于备赛蓝桥杯Web应用开发)
开发语言·前端·职场和发展·蓝桥杯·es6·css3·html5