手写简易Promise,吃透异步底层核心原理
相信绝大多数前端开发者日常开发都在高频使用 Promise。不管是接口请求、定时器封装、异步任务编排基本离不开它。但我面试过程中发现一个普遍问题大部分人只会用 Promise完全不懂底层原理。调用 resolve、reject、写 then 链式调用人人都会但一问到 Promise 三种状态、为什么能链式调用、异步回调怎么缓存的很多人直接卡壳。其实想要真正吃透 JS 异步、看懂 EventLoop 执行顺序手写一遍简易 Promise 是最快的捷径。不用写符合 PromiseA 完整规范的复杂源码我们手写一个极简版就能彻底搞懂核心机制。今天就带大家从零手写边写边解析彻底吃透异步底层。一、先理清 Promise 三大核心特性手写之前必须先记住 Promise 铁律所有源码逻辑都是围绕这几条规则写的1、三种状态pending等待、fulfilled成功、rejected失败2、状态不可逆一旦从 pending 变为成功/失败就永久固定无法二次修改3、支持 then 链式调用支持异步回调缓存。很多人写不懂源码就是忽略了状态不可逆这个关键点。二、搭建基础 Promise 结构我们先实现最基础的功能状态管理、resolve、reject 触发、阻止状态二次修改。class MyPromise { // 初始状态为等待 status pending; // 成功返回值 value null; // 失败原因 reason null; constructor(executor) { // 立即执行传入的执行器函数 executor(this.resolve, this.reject); } // 成功回调 resolve (value) { // 状态不是pending直接拦截保证不可逆 if(this.status ! pending) return; this.status fulfilled; this.value value; }; // 失败回调 reject (reason) { if(this.status ! pending) return; this.status rejected; this.reason reason; }; }到这里我们已经实现了最核心的状态锁定功能。测试一下效果const p new MyPromise((resolve, reject) { resolve(请求成功); // 第二次修改无效状态锁定 reject(失败); }); console.log(p);可以看到执行 resolve 后状态固定为 fulfilled后续 reject 完全失效符合原生 Promise 特性。三、实现核心 then 方法同步场景Promise 最核心的就是 then 方法根据不同状态执行不同回调。我们先实现同步场景的 thenthen(onFulfilled, onRejected) { // 成功状态执行成功回调 if(this.status fulfilled){ onFulfilled(this.value); } // 失败状态执行失败回调 if(this.status rejected){ onRejected(this.reason); } }现在测试同步场景完全正常new MyPromise((resolve) { resolve(数据加载成功); }).then(res { console.log(res); });四、解决异步场景缓存回调函数重点上面代码只能处理同步代码如果遇到定时器、异步请求就会失效。举个例子执行器内部有异步延迟代码走完 then 的时候状态还是 pending回调直接不执行。这也是新手手写 Promise 最大的坑。解决方案pending 状态时缓存回调函数等状态变更后再执行。完善完整可运行源码class MyPromise { status pending; value null; reason null; // 缓存成功、失败回调 successCallback null; errorCallback null; constructor(executor) { executor(this.resolve, this.reject); } resolve (value) { if(this.status ! pending) return; this.status fulfilled; this.value value; // 异步场景状态变更后执行缓存的回调 this.successCallback this.successCallback(this.value); }; reject (reason) { if(this.status ! pending) return; this.status rejected; this.reason reason; this.errorCallback this.errorCallback(this.reason); }; then(onFulfilled, onRejected) { if(this.status fulfilled){ onFulfilled(this.value); }else if(this.status rejected){ onRejected(this.reason); }else{ // 异步等待中缓存回调 this.successCallback onFulfilled; this.errorCallback onRejected; } } }测试异步场景完美生效new MyPromise((resolve) { setTimeout(() { resolve(异步数据返回); }, 1000); }).then(res { console.log(res); });五、实现链式调用精髓原生 Promise 支持 then 链式调用核心原理就是then 方法返回一个新的 Promise。我们简单改造 then实现基础链式能力让代码更贴近原生then(onFulfilled, onRejected) { return new MyPromise((resolve) { if(this.status fulfilled){ let result onFulfilled(this.value); // 将返回值传给下一个then resolve(result); } // 省略rejected、pending逻辑... }) }到这里一个极简可用、覆盖90%业务场景的手写 Promise 就完成了。虽然没有完整兼容 PromiseA 规范微任务、穿透、异常捕获但核心底层逻辑完全一致。六、手写 Promise 必须搞懂的核心知识点1、Promise 本身是同步立即执行的只有 then 回调是异步微任务2、状态不可逆是 Promise 稳定性的根本防止多次触发回调3、异步回调靠「缓存函数」实现这是很多人看不懂源码的关键点4、链式调用的本质是 then 返回新 Promise实现任务串联。七、为什么一定要手写 Promise很多同学觉得手写 Promise 没用工作中直接用原生就行。其实不然手写一遍最大的意义是打通 JS 异步思维。平时搞不懂的微任务执行顺序、then 执行时机、链式穿透、异常冒泡手写完源码全部豁然开朗。后续看 async/await、EventLoop、异步报错排查都会轻松很多面试更是高频加分项。总结本文实现的简易 Promise保留了最核心的底层逻辑剔除了复杂的规范兼容代码非常适合新手入门学习。不用死记硬背概念跟着代码一步步跑就能彻底理解异步执行机制。想要进阶的同学可以基于本文代码继续扩展异常捕获、微任务封装、链式穿透、all/race 静态方法逐步完善成完整 PromiseA 版本彻底吃透 JS 异步底层。

相关新闻