重温ES6经典之模拟实现Promise
2023-12-03 03:43:47
前言
ES6的出现为JavaScript带来了众多激动人心的新特性,其中Promise作为异步编程的利器,以其简洁优雅的语法和强大的功能赢得了广大开发者的青睐。Promise的出现,为我们提供了更加高效、可读性更强的异步编程方式,使我们能够轻松处理复杂的异步操作。
Promise的模拟实现
为了更好地理解Promise的运作机制,我们不妨从头开始,模拟实现一个简单的Promise。
首先,我们需要定义一个Promise构造函数。Promise构造函数接受一个函数作为参数,该函数将作为Promise的执行器。执行器有两个参数,分别是resolve和reject,这两个函数用于分别将Promise的状态变为fulfilled和rejected。
function Promise(executor) {
this.state = "pending"; // 初始状态为pending
this.value = null; // 存储成功结果
this.reason = null; // 存储失败结果
this.onFulfilledCallbacks = []; // 存放成功的回调函数
this.onRejectedCallbacks = []; // 存放失败的回调函数
const resolve = (value) => {
if (this.state === "pending") {
this.state = "fulfilled";
this.value = value;
this.onFulfilledCallbacks.forEach((callback) => {
callback(value);
});
}
};
const reject = (reason) => {
if (this.state === "pending") {
this.state = "rejected";
this.reason = reason;
this.onRejectedCallbacks.forEach((callback) => {
callback(reason);
});
}
};
executor(resolve, reject);
}
Promise构造函数创建了一个新的Promise实例,并将其状态初始化为“pending”。当执行器调用resolve或reject时,Promise的状态将分别变为“fulfilled”或“rejected”。同时,根据Promise的状态,将成功的回调函数或失败的回调函数加入对应的回调函数数组中。
接下来,我们需要定义一个then
方法,它将作为Promise实例的方法供外部调用。then
方法接受两个参数,分别是成功的回调函数和失败的回调函数。当Promise的状态变为“fulfilled”时,将调用成功的回调函数,并将Promise的成功结果作为参数传递给该函数。当Promise的状态变为“rejected”时,将调用失败的回调函数,并将Promise的失败结果作为参数传递给该函数。
Promise.prototype.then = function (onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
if (this.state === "fulfilled") {
setTimeout(() => {
try {
const value = onFulfilled(this.value);
resolve(value);
} catch (error) {
reject(error);
}
}, 0);
} else if (this.state === "rejected") {
setTimeout(() => {
try {
const reason = onRejected(this.reason);
resolve(reason);
} catch (error) {
reject(error);
}
}, 0);
} else {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const value = onFulfilled(this.value);
resolve(value);
} catch (error) {
reject(error);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const reason = onRejected(this.reason);
resolve(reason);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
};
then
方法返回一个新的Promise实例,该Promise实例的状态取决于传入的回调函数的返回值。如果成功的回调函数返回一个Promise,则新的Promise实例的状态取决于该Promise的状态。如果成功的回调函数返回一个普通值,则新的Promise实例的状态变为“fulfilled”,并将该普通值作为新的Promise实例的成功结果。如果失败的回调函数返回一个Promise,则新的Promise实例的状态取决于该Promise的状态。如果失败的回调函数返回一个普通值,则新的Promise实例的状态变为“rejected”,并将该普通值作为新的Promise实例的失败结果。
需要注意的是,由于JavaScript的异步特性,回调函数的执行顺序是无法确定的。为了避免出现执行顺序混乱的情况,我们使用setTimeout
将回调函数的执行推迟到下一个事件循环中。这样,我们可以确保回调函数总是按照正确的顺序执行。
Promises/A+规范的实现
Promises/A+规范是Promise的官方规范,它定义了Promise必须遵守的标准。为了确保我们的Promise实现与Promises/A+规范兼容,我们需要对代码进行一些修改。
首先,我们需要定义一个新的PromiseResolver
类,该类将作为Promise的执行器。PromiseResolver
类包含两个属性,分别是promise
和resolve
。promise
属性保存着当前的Promise实例,resolve
属性保存着resolve
函数。
class PromiseResolver {
constructor() {
this.promise = new Promise((resolve) => {
this.resolve = resolve;
});
}
}
接下来,我们需要修改Promise构造函数,使其使用PromiseResolver
类来创建Promise实例。
function Promise(executor) {
const resolver = new PromiseResolver();
const resolve = (value) => {
resolver.resolve(value);
};
const reject = (reason) => {
resolver.reject(reason);
};
executor(resolve, reject);
return resolver.promise;
}
这样,我们就确保了Promise实例总是按照Promises/A+规范创建。
此外,我们需要对then
方法进行一些修改,以使其符合Promises/A+规范。
Promise.prototype.then = function (onFulfilled, onRejected) {
return this._then(onFulfilled, onRejected);
};
Promise.prototype._then = function (onFulfilled, onRejected) {
const self = this;
return new Promise((resolve, reject) => {
const fulfilledHandler = (value) => {
try {
const result = onFulfilled ? onFulfilled(value) : value;
resolve(result);
} catch (error) {
reject(error);
}
};
const rejectedHandler = (reason) => {
try {
const result = onRejected ? onRejected(reason) : reason;
resolve(result);
} catch (error) {
reject(error);
}
};
if (self.state === "fulfilled") {
setTimeout(() => {
fulfilledHandler(self.value);
}, 0);
} else if (self.state === "rejected") {
setTimeout(() => {
rejectedHandler(self.reason);
}, 0);
} else {
self.onFulfilledCallbacks.push(fulfilledHandler);
self.onRejectedCallbacks.push(rejectedHandler);
}
});
};
修改后的then
方法与Promises/A+规范完全兼容,它可以正确处理各种情况,并确保回调函数总是按照正确的顺序执行。
结语
通过模拟实现Promise,我们不仅对Promise的运作机制有了更深入的理解,也对Promises/A+规范有了更加全面的认识。通过手动构建Promise,我们能够更加深刻地体会到Promise的强大之处以及异步编程的精髓。如果您想要在项目中使用Promise,建议您使用成熟的Promise库,如bluebird
、q
或native-promises
,以避免出现兼容性问题和性能问题。