手撕面试官...啊,不,手撕Promise
2023-09-20 03:35:30
Promise详解
Promise是JavaScript中用于处理异步编程的利器,它可以让你以一种同步的方式处理异步操作的结果。为了理解Promise的原理,我们需要先了解一下JavaScript的事件循环机制。
事件循环
JavaScript的事件循环是一个单线程循环,它不断地从任务队列中取出任务并执行。任务队列是一个先进先出的队列,这意味着先进入队列的任务会先被执行。
当JavaScript代码执行时,它会将任务添加到任务队列中。当任务队列不为空时,JavaScript引擎会从队列中取出第一个任务并执行它。如果任务是一个同步任务,它会立即执行并完成。如果任务是一个异步任务,它会先被添加到一个单独的异步任务队列中,然后在适当的时候被执行。
回调函数
在JavaScript中,回调函数是指当某个异步操作完成后被调用的函数。例如,当一个HTTP请求完成后,我们可以使用回调函数来处理服务器返回的数据。
回调函数是一个非常有用的工具,但它也有一个缺点:它会导致代码难以阅读和维护。想象一下,如果你在一个程序中使用了多个异步操作,那么你的代码中就会充斥着大量的回调函数,这会让你的代码变得非常混乱。
Promise
Promise正是为了解决回调函数的缺点而诞生的。Promise是一个对象,它代表了一个异步操作的结果。当异步操作完成后,Promise会进入已完成状态,并调用注册的回调函数来处理结果。
与回调函数相比,Promise有以下优点:
- 代码更易读、更易维护。 Promise使用链式调用来处理异步操作的结果,这使得代码更加清晰易懂。
- 可以更好地处理错误。 Promise可以捕获异步操作中发生的错误,并将其传递给注册的回调函数。
- 可以组合多个异步操作。 Promise可以很容易地组合多个异步操作,并以一种同步的方式处理它们的结果。
手写Promise
现在,我们已经对Promise有了基本的了解,接下来我们将手把手教你如何用JavaScript从头开始构建一个自己的Promise。
构造函数
首先,我们需要定义一个Promise的构造函数。Promise构造函数接收一个参数,该参数是一个函数,称为执行器函数(executor)。执行器函数有两个参数,分别是resolve和reject,这两个函数用于将Promise的状态从挂起状态变为已完成状态。
function Promise(executor) {
this.state = 'pending'; // 初始状态为挂起
this.value = undefined; // 结果值
this.reason = undefined; // 拒绝原因
this.onFulfilledCallbacks = []; // 存放已完成时的回调函数
this.onRejectedCallbacks = []; // 存放已拒绝时的回调函数
// 执行器函数立即执行,用于初始化Promise的状态
executor(resolve, reject);
}
resolve和reject函数
resolve函数用于将Promise的状态从挂起状态变为已完成状态,并传递一个值作为结果。reject函数用于将Promise的状态从挂起状态变为已拒绝状态,并传递一个原因作为拒绝原因。
function resolve(value) {
// 将状态从挂起变为已完成
this.state = 'fulfilled';
// 将结果值保存起来
this.value = value;
// 依次调用已完成时的回调函数
this.onFulfilledCallbacks.forEach(callback => callback(value));
}
function reject(reason) {
// 将状态从挂起变为已拒绝
this.state = 'rejected';
// 将拒绝原因保存起来
this.reason = reason;
// 依次调用已拒绝时的回调函数
this.onRejectedCallbacks.forEach(callback => callback(reason));
}
then方法
then方法用于在Promise完成后执行指定的回调函数。then方法接收两个参数,分别是onFulfilled和onRejected,这两个参数都是函数,分别用于处理已完成和已拒绝的情况。
Promise.prototype.then = function(onFulfilled, onRejected) {
// 如果没有传递onFulfilled,则使用一个默认函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
// 如果没有传递onRejected,则使用一个默认函数
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
// 根据Promise的状态,将回调函数添加到相应的数组中
if (this.state === 'fulfilled') {
setTimeout(() => {
onFulfilled(this.value);
}, 0);
} else if (this.state === 'rejected') {
setTimeout(() => {
onRejected(this.reason);
}, 0);
} else {
// 如果Promise的状态还是挂起,则将回调函数添加到相应的数组中,等待状态改变后再执行
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
}
// 返回一个新的Promise,以便可以继续链式调用
return new Promise((resolve, reject) => {
this.then(value => {
try {
const result = onFulfilled(value);
resolve(result);
} catch (error) {
reject(error);
}
}, reason => {
try {
const result = onRejected(reason);
resolve(result);
} catch (error) {
reject(error);
}
});
});
};
catch方法
catch方法用于处理Promise被拒绝的情况。catch方法接收一个参数,该参数是一个函数,用于处理已拒绝的情况。
Promise.prototype.catch = function(onRejected) {
return this.then(null, onRejected);
};
finally方法
finally方法用于无论Promise是已完成还是已拒绝都会执行的回调函数。finally方法接收一个参数,该参数是一个函数,用于处理无论Promise是已完成还是已拒绝的情况。
Promise.prototype.finally = function(onFinally) {
return this.then(value => {
onFinally();
return value;
}, reason => {
onFinally();
throw reason;
});
};
结语
至此,我们已经手把手地实现了一个Promise。通过对Promise的深入理解和手写实现,你对异步编程和Promise有了更深入的认识。在实际开发中,你可以使用原生Promise或第三方Promise库来处理异步操作,这将使你的代码更加清晰易懂、更易维护。