手撕Promise:Promise.then详解

前几天我们在手撕Promise 源码篇的时候,对于Promise.then 的实现只是给出了带有注解的源代码,那么让我们一步一步揭开.then的神秘面纱,如何实现.then的链式调用呢?

官方的Promise.then

let p1 = new Promise((resolve, reject) => {resolve(123); 
})
// Promise {<fulfilled>: 123}
p1.then(res => console.log(res,'success'),err => console.log(err,'failed')
)
// 123 'success'
// Promise {<fulfilled>: undefined} 

Promise.then的链式调用

let p1 = new Promise((resolve, reject) => {resolve(123); 
})
.then(res => res * 3);
.then(res => res * 3);
.then(res => {console.log(res);// 1107
}) 

从以上两个例子我们可以看到,Promise.then方法接收两个回调函数作为参数,第一个参数是fulfilled状态的回调函数,第二个参数是rejected状态的回调函数,当promise的状态为'fulfilled'会执行第一个回调函数,当状态为'rejected'时执行第二个回调函数。

这并不意味着'pending'状态的Promise对象不能在后面接.then,而是当Promsie的状态变更为'fulfilled'或者'rejected'状态的时候,才会触发里面两个回调函数其中一个,并且.then支持链式调用,链式传参,所以上一次的.then的结果会影响到下一个.then。

知道以上这些我们就可以试试来自己实现Promsie.then,在看我如何手撕Promsie这篇文章中我们已经手把手教大家实现了resolvereject两个方法,这里我们就直接接着前一篇文章的进度,不明白这两个方法怎么实现的朋友可以去看看之前的文章。

class myPromise {constructor(executor) {this.status = 'pending'; // 变更promise的状态this.value = null;try { executor(this.resolve.bind(this), this.reject.bind(this)); // new 一个myPromise 得到的实例对象里面有两个函数} catch (error) { this.reject(error) } }resolve(value) {if (this.status !== 'pending') return;this.status = 'fulfilled'; // 变更promise的状态this.value = value;}reject(reason) {if (this.status !== 'pending') returnthis.status = 'rejected';this.value = reason;}
} 

首先.then返回的也是一个Promise对象,所以我们第一步

then(onFulfilled, onRejected) {return new myPromise((resolve, reject) => {} 

第二步.then的执行虽然不受我们控制,但是其内部的回调的执行取决于'Promise的状态',只要Promise的状态处于 'pending' 状态,就算后面接了再多的.then都不会执行,所以我们要将成功或者失败的回调都使用两个队列存起来,等到Promise的状态变更的时候再根据队列先进先出的特性,依次将队列中的回调执行。

我们在构造器函数中定义了两个队列 (只使用push和shift方法,即可人为认定队列,或者使用pop和unshift方法,原理相同) onFulfilledCallbacksonRejectedCallbacks,这样我们在调用resolve或者reject方法的时候,只需要判断,相应的回调函数队列中是否有未执行的函数,使用shift方法切出来之后执行。

 constructor(executor) {this.status = 'pending'; this.value = null;this.onFulfilledCallbacks = []; // 用来保存成功的回调(处理异步)this.onRejectedCallbacks = []; // 用来保存失败的回调(处理异步)try {executor(this.resolve.bind(this), this.reject.bind(this));} catch (error) {this.reject(error)}}resolve(value) {if (this.status !== 'pending') return;this.status = 'fulfilled';this.value = value;// 调用then里面的回调while (this.onFulfilledCallbacks.length) {// 判断是否存在未执行的回调函数this.onFulfilledCallbacks.shift()(this.value)// 存在则切出来执行掉}}reject(reason) {if (this.status !== 'pending') returnthis.status = 'rejected';this.value = reason;while (this.onRejectedCallbacks.length) {// 判断是否存在未执行的回调函数this.onRejectedCallbacks.shift()(this.value)// 存在则切出来执行掉}} 

第三步,在.then中我们需要判断Promise的状态,当状态为'fulfilled'则调用resolve方法,状态为'rejected'则调用'reject'方法,如果状态为'pending'那就将回调存到对应的回调队列中去。

因为不能将Promise对象本身作为回调参数,所以我们将要返回的Promise对象声明一个变量,以便于判断是否将自身作为回调传进来,如果将自身作为回调我们就是抛出一个错误,如果参数就是一个新的Promise对象,则直接调用.then

then(onFulfilled, onRejected) { var thenPromise = new myPromise((resolve, reject) => {const resolvePromise = callback => {try {const x = callback(this.value);// 声明回调的调用if (x === thenPromise) {// 你正在返回自身throw new Error('不允许返回自身!');}if (x instanceof myPromise) { // 返回的是一个Promise对象x.then(resolve, reject);} else { // 直接返回一个值,作为resolve的值,传递给下一个.thenresolve(x);}} catch (error) {reject(error);throw new Error(error);}})}if (this.status === 'fulfilled') {resolvePromise(onFulfilled)} else if (this.status === 'rejected') {resolvePromise(onRejected)} else if (this.status === 'pending') {this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled));// 考虑this的指向问题,直接使用bind方法显示绑定this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected));// 考虑this的指向问题,直接使用bind方法显示绑定}})return thenPromise} 

最后一步,我们要知道Promise.then是属于异步代码中的微任务事件,其执行顺序会在宏任务之后执行,但是我们目前实现的效果是.then这一步的逻辑还是同步代码,所以我们在返回的Promise对象的外面套一层setTimeout,这样返回出去的.then的逻辑,会去到下一次的宏任务队列,这样就实现了.then的执行会比同步代码稍晚一些,但是官方并不是使用setTimeout实现的。

完整版then方法代码:

class myPromise {constructor(executor) {this.status = 'pending'; this.value = null;this.onFulfilledCallbacks = []; // 用来保存成功的回调(处理异步)this.onRejectedCallbacks = []; // 用来保存失败的回调(处理异步)try {executor(this.resolve.bind(this), this.reject.bind(this));} catch (error) {this.reject(error)}}resolve(value) {if (this.status !== 'pending') return;this.status = 'fulfilled';this.value = value;// 调用then里面的回调while (this.onFulfilledCallbacks.length) {this.onFulfilledCallbacks.shift()(this.value)}}reject(reason) {if (this.status !== 'pending') returnthis.status = 'rejected';this.value = reason;while (this.onRejectedCallbacks.length) {this.onRejectedCallbacks.shift()(this.value)}}then(onFulfilled, onRejected) {onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => valonRejected = typeof onRejected === 'function' ? onRejected : val => { throw val }var thenPromise = new myPromise((resolve, reject) => {const resolvePromise = callback => {setTimeout(() => { // 让整个回调函数比同步代码晚一点执行,官方不是使用setTimeout实现try {const x = callback(this.value);if (x === thenPromise) {// 你正在返回自身throw new Error('不允许返回自身!');}if (x instanceof myPromise) { // 返回的是一个Promise对象x.then(resolve, reject);} else { // 直接返回一个值,作为resolve的值,传递给下一个.thenresolve(x);}} catch (error) {reject(error);throw new Error(error);}})}if (this.status === 'fulfilled') {resolvePromise(onFulfilled)} else if (this.status === 'rejected') {resolvePromise(onRejected)} else if (this.status === 'pending') {this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled));this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected));}})return thenPromise}
} 

让我们看看最终的效果:

希望这篇文章能够帮助到大家去更好的理解.then的实现原理,有任何不足的之处可以在评论区留言,大家共同进步!

最后

整理了75个JS高频面试题,并给出了答案和解析,基本上可以保证你能应付面试官关于JS的提问。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

<think>嗯,用户想了解如何动实现JavaScriptPromise。首先,我得回忆一下Promise的基本工作原理。Promise是用于处理异步操作的对象,有三种状态:pending、fulfilled和rejected。状态一旦改变就不能再变。 首先,我需要定义一个类,比如MyPromise。构造函数接收一个执行器函数,执行器里会调用resolve和reject。这两个函数需要改变状态,并且处理回调。因为then方法可以多次调用,所以需要存储回调函数,可能用数组来保存。 然后,resolve和reject函数要负责改变状态,并且执行相应的回调。这里要注意,异步操作完成后才会触发回调,所以可能需要用setTimeout或者微任务机制来异步执行回调。不过动实现的话,可能先用setTimeout模拟。 接下来是then方法的实现。then方法需要接收onFulfilled和onRejected两个参数,并且返回一个新的Promise,以实现链式调用。这时候要考虑如果onFulfilled不是函数的话,应该设置默认函数,将值传递下去。同时,需要处理回调的执行结果,如果是Promise的话,需要等待它的状态改变。 另外,错误处理也很重要。如果在执行过程中抛出异常,应该捕获并传递给下一个reject。比如在执行器函数或者then的回调中出现的错误,都需要被正确处理。 还要处理同步和异步的情况。比如,如果执行器里同步调用了resolve,那么then方法添加的回调应该还是异步执行。这可能需要用队列来管理回调,确保它们按顺序执行。 可能还需要实现catch方法,其实就是then方法的特例,只处理rejected的情况。还有finally方法,不过用户的问题可能暂时不需要这个。 测试的时候,用户给出的例子中有多个then调用,并且有setTimeout。所以在实现时,必须确保then的回调在resolve被调用后才会执行,并且多个then的回调都能被正确触发。 此外,链式调用中的返回值如果是Promise,需要等待它解决。这部分可能需要递归处理,直到得到一个非Promise的值,或者最终状态。 可能遇到的难点包括状态管理的原子性,确保一旦状态改变就不会再变,以及正确处理异步回调的顺序。还有链式调用中每个then返回新Promise的处理逻辑,需要正确地将前一个的结果传递给下一个。 总结步骤大概是: 1. 定义MyPromise类,初始化状态和值。 2. 实现resolve和reject方法,改变状态并执行回调。 3. 实现then方法,收集回调,返回新Promise。 4. 处理回调的返回值,支持链式调用。 5. 错误处理和异步执行。 需要参考用户提供的引用,比如引用[3]中的例子,确保实现的MyPromise能正确处理多个then的调用,并在setTimeout后执行。同时,引用[2]提到then返回Promise,所以链式调用需要正确处理返回的Promise。 可能还需要实现其他方法如catch和静态方法如resolve、reject、all等,但根据用户的问题,主要关注then和基本结构。 最后,代码示例要符合规范,正确使用$和$$格式,确保LaTeX语法正确,比如在提到状态时用$...$包裹变量。</think>### 动实现 JavaScript Promise 的方法 #### 1. Promise 的核心原理 Promise 是用于管理异步操作的对象,包含三种状态:$pending$(等待)、$fulfilled$(成功)和$rejected$(失败)。状态一旦改变不可逆。实现需满足以下特性: - **链式调用**:通过 `then` 方法返回新 Promise 实现链式操作[^2] - **异步执行**:回调函数需异步触发(如通过 `setTimeout` 模拟微任务) - **错误冒泡**:若未定义错误处理,错误会向下传递直到被捕获 #### 2. 基础框架实现 ```javascript class MyPromise { constructor(executor) { this.state = 'pending'; // 初始状态 this.value = undefined; // 成功值/失败原因 this.onFulfilledCallbacks = []; // 成功回调队列 this.onRejectedCallbacks = []; // 失败回调队列 const resolve = (value) => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; this.onFulfilledCallbacks.forEach(fn => fn()); // 异步触发回调 } }; const reject = (reason) => { if (this.state === 'pending') { this.state = 'rejected'; this.value = reason; this.onRejectedCallbacks.forEach(fn => fn()); } }; try { executor(resolve, reject); // 立即执行执行器函数 } catch (err) { reject(err); // 捕获同步错误 } } } ``` #### 3. `then` 方法实现 ```javascript then(onFulfilled, onRejected) { // 默认值穿透处理 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v; onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }; const promise2 = new MyPromise((resolve, reject) => { const handleCallback = (callback, value) => { setTimeout(() => { // 模拟微任务 try { const x = callback(value); resolvePromise(promise2, x, resolve, reject); } catch (err) { reject(err); } }); }; if (this.state === 'fulfilled') { handleCallback(onFulfilled, this.value); } else if (this.state === 'rejected') { handleCallback(onRejected, this.value); } else { // pending 状态时存储回调 this.onFulfilledCallbacks.push(() => handleCallback(onFulfilled, this.value)); this.onRejectedCallbacks.push(() => handleCallback(onRejected, this.value)); } }); return promise2; // 返回新 Promise 实现链式调用 } ``` #### 4. 解决回调返回值 ```javascript function resolvePromise(promise2, x, resolve, reject) { if (x === promise2) { return reject(new TypeError('Chaining cycle detected')); } if (x instanceof MyPromise) { x.then(resolve, reject); // 递归处理 Promise 返回值 } else { resolve(x); // 直接传递普通值 } } ``` #### 5. 测试用例 ```javascript const p = new MyPromise((resolve) => { setTimeout(() => resolve("ok"), 1000); }); p.then(res => console.log(res + 1)) // 输出 "ok1" .then(() => new MyPromise(resolve => resolve(100))) .then(num => console.log(num)); // 输出 100 ``` #### 关键点总结 - **状态隔离**:每个 `then` 返回的新 Promise 独立管理状态 - **异步队列**:通过回调队列处理异步操作和多次调用 `then` 的场景[^3] - **错误处理**:通过 `try/catch` 捕获同步/异步错误 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值