手写 Promise() 相关函数:
Promise()、then()、catch()、finally()
// 定义三种状态常量constPENDING='pending'constFULFILLED='fulfilled'constREJECTED='rejected'classMyPromise{/* 定义状态和结果两个私有属性: 1.使用 # 语法(ES2022+ 官方私有字段):在类中通过 # 前缀声明属性,该属性仅在类的内部可访问 2.Symbol 作为属性键:用 Symbol 作为属性名,外部无法直接获取 Symbol 引用。 */#state=PENDING// 状态#result=undefined// 结果#thenables=[]// 存储 then 方法回调的队列constructor(executor){// resolve 和 reject 主要功能即为改变状态,设置结果constresolve=(value)=>{// 解决时调用this.#changeState(FULFILLED,value)}constreject=(reason)=>{// 拒绝时调用this.#changeState(REJECTED,reason)}try{// 只能捕获同步错误,无法捕获异步错误,如 setTimeout 里的错误executor(resolve,reject)}catch(err){reject(err)}}// 改变状态和设置结果的私有方法#changeState(state,result){if(this.#state!==PENDING)returnthis.#state=statethis.#result=resultthis.#run()}// 处理 then 回调的私有方法#handleCallback(callback,resolve,reject){if(typeofcallback!=='function'){// 状态穿透,即 then 方法返回的 Promise 状态与当前 Promise 状态保持一致// then 回调是微任务,需要放到微任务队列中执行queueMicrotask(()=>{// 参考:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/queueMicrotaskconstsettled=this.#state===FULFILLED?resolve:rejectsettled(this.#result)})return}queueMicrotask(()=>{try{constresult=callback(this.#result)// 如果返回值是一个 Promise,则等待它完成后再 resolveif(resultinstanceofMyPromise){result.then(resolve,reject)}else{resolve(result)}}catch(err){reject(err)}})}/* 执行队列的私有方法,两个执行时机: 1.Promise 状态改变时 2.then 方法被调用时 */#run(){if(this.#state===PENDING)return// 取出队列中的回调函数,依次执行(先进先出原则)while(this.#thenables.length){const{onFulfilled,onRejected,resolve,reject}=this.#thenables.shift()try{if(this.#state===FULFILLED){// 执行 onFulfilled 回调函数this.#handleCallback(onFulfilled,resolve,reject)}else{// 执行 onRejected 回调函数this.#handleCallback(onRejected,resolve,reject)}}catch(err){reject(err)}}}/* onFulfilled 可选:一个在此 Promise 对象被兑现时异步执行的函数。它的返回值将成为 then() 返回的 Promise 对象的兑现值。 onRejected 可选:一个在此 Promise 对象被拒绝时异步执行的函数。它的返回值将成为 catch() 返回的 Promise 对象的兑现值。 */then(onFulfilled,onRejected){returnnewMyPromise((resolve,reject)=>{// 将四个回调函数放入队列,以便立即或将来处理this.#thenables.push({onFulfilled,onRejected,resolve,reject})// 启动队列处理this.#run()})}catch(onRejected){returnthis.then(undefined,onRejected)}finally(onFinally){this.then((value)=>{MyPromise.resolve(onFinally()).then(()=>value)},(reason)=>{MyPromise.resolve(onFinally()).then(()=>{throwreason})})}}验证测试 MyPromise() 函数
constp1=newPromise((resolve,reject)=>{resolve(1)reject(2)})console.log('p1',p1)constp2=newPromise(()=>{throw123})console.log('p2',p2)constp3=newPromise((resolve,reject)=>{setTimeout(()=>{resolve('setTimeout')// 无法捕获},0)})console.log('p3',p3)constmyP1=newMyPromise((resolve,reject)=>{resolve(1)reject(2)})console.log('myP1',myP1)myP1.then((res)=>{console.log('res',res)// 1returnres+1},(err)=>{console.log('err',err)// 1}).then((res)=>{console.log('res',res)// 2// throw 'error'returnres+1}).then((res)=>{console.log('res',res)// 3}).catch((err)=>{console.log('err',err)}).finally(()=>{console.log('finally')})constmyP2=newMyPromise(()=>{throw123})console.log('myP2',myP2)constmyP3=newMyPromise((resolve,reject)=>{setTimeout(()=>{resolve('setTimeout')// 无法捕获},0)})console.log('myP3',myP3)