JS手撕代码系列【手写实现Promise】
在 JavaScript 中,Promise 是一种用于处理异步操作的对象,它可以让异步代码更易于理解和管理。在本文中,我们将手写实现一个 Promise。
Promise 的基本概念
在介绍 Promise 的实现方法之前,让我们先回顾一下 Promise 的基本概念。
Promise 有三种状态:
- Pending(进行中):初始状态,表示 Promise 实例正在进行中。
- Fulfilled(已完成):表示异步操作已经完成,并且 Promise 实例已经获得了一个值。
- Rejected(已失败):表示异步操作已经失败,并且 Promise 实例已经获得了一个失败原因。
当 Promise 实例从 Pending 状态转变为 Fulfilled 或者 Rejected 状态时,我们称之为 Promise 已经 settled。
Promise 的实现
我们将使用 ES6 的类来实现一个 Promise。Promise 类需要具备以下功能:
- 接受一个 executor 函数作为参数,该函数将在 Promise 实例创建时立即执行,并传递两个回调函数,即 resolve 和 reject 函数。
- 在 executor 函数内部,我们需要处理异步操作,如果操作成功,则使用 resolve 函数返回结果;如果操作失败,则使用 reject 函数返回错误信息。
- Promise 实例需要提供 then 方法,该方法接受两个回调函数,即 onFulfilled 和 onRejected,分别在 Promise 转变为 Fulfilled 或者 Rejected 状态时执行。
- 如果在 executor 函数执行过程中发生错误,Promise 实例应该转变为 Rejected 状态,并且使用 reject 函数返回错误信息。
- Promise 实例可以链式调用 then 方法,每个 then 方法可以返回一个新的 Promise 实例,实现 Promise 链。
下面是我们的 Promise 类的基本实现:
class Promise {
constructor(executor) {
this.status = 'pending';
this.value = null;
this.reason = null;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.status === 'pending') {
this.status = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach((callback) => {
callback(this.value);
});
}
};
const reject = (reason) => {
if (this.status === 'pending') {
this.status = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach((callback) => {
callback(this.reason);
});
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
const newPromise = new Promise((resolve, reject) => {
if (this.status === 'fulfilled') {
setTimeout(() => {
try {
const result = onFulfilled(this.value);
resolve(result);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.status === 'rejected') {
setTimeout(() => {
try {
const result = onRejected(this.reason);
resolve(result);
} catch (error) {
reject(error);
}
}, 0);
}
if(this.status === 'pending') {
this.onFulfilledCallbacks.push((value) => {
setTimeout(() => {
try {
const result = onFulfilled(value);
resolve(result);
} catch (error) {
reject(error);
}
}, 0);
});
this.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
try {
const result = onRejected(reason);
resolve(result);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return newPromise;
}
}
我们先创建了一个 Promise 类,其中包含了 Promise 的基本属性和方法。在构造函数中,我们定义了 status、value、reason 三个变量用于保存 Promise 的状态和值,以及 onFulfilledCallbacks 和 onRejectedCallbacks 两个数组用于保存 then 方法中传入的回调函数。
然后,我们创建了 resolve 和 reject 两个函数,分别用于处理 Promise 的 Fulfilled 和 Rejected 状态。在 try-catch 语句中,我们调用了 executor 函数,并将 resolve 和 reject 函数传入,以便在异步操作完成后调用。
接下来,我们实现了 then 方法。当 Promise 的状态为 Fulfilled 时,我们使用 setTimeout 函数将 onFulfilled 回调函数放入微任务队列中,以确保在本轮事件循环结束后执行。同样的,当 Promise 的状态为 Rejected 时,我们使用 setTimeout 函数将 onRejected 回调函数放入微任务队列中。
当 Promise 的状态为 Pending 时,我们将 onFulfilled 和 onRejected 回调函数保存到 onFulfilledCallbacks 和 onRejectedCallbacks 数组中,并在异步操作完成后执行。
最后,我们返回一个新的 Promise 实例,该实例包含了一个 executor 函数。在 executor 函数中,我们根据当前 Promise 的状态决定调用 onFulfilled 或 onRejected 回调函数,并将执行结果传递给 resolve 或 reject 函数。
使用手写Promise
现在,我们可以使用手写的 Promise 类来处理异步操作了。下面是一个简单的例子:
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hello, world!');
}, 1000);
});
promise
.then((result) => {
console.log(result);
return 'Goodbye, world!';
})
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(error);
});
在这个例子中,我们创建了一个 Promise 实例,并在 1 秒钟后将状态设置为 Fulfilled,并返回一个字符串。
然后,我们链式调用了两个 then 方法,第一个方法打印了字符串并返回了一个新的字符串,第二个方法又打印了新的字符串。
最后,我们调用了 catch 方法,以处理任何可能发生的错误。
结论
Promise 是一种非常强大的异步编程技术,可以使我们更加轻松地管理异步代码。通过手写实现 Promise,我们可以更好地理解 Promise 的工作原理,并更加深入地了解 JavaScript 的异步编程模型。