手写Promise:从菜鸟到进坑的实战指南
2023-09-24 07:19:52
手写Promise从入门到入坑
前言
作为一名开发小白,我曾天真地以为,Promise不过是异步编程的垫脚石,轻轻松松就能掌握。然而,当自己动手尝试编写Promise时,才发现这远比想象中困难。从早到晚地肝代码,却始终不得要领。这不禁让我感慨:我终究还是太菜了。
从零开始
什么是Promise?
Promise是一个表示异步操作结果的特殊对象。当异步操作成功时,Promise会以成功的值resolve,当操作失败时,Promise会以失败的原因reject。
Promise的链式调用
Promise最强大的特性之一是链式调用,它可以串联多个异步操作,并根据前一个操作的结果决定后续操作。链式调用通过then方法实现,它返回一个新的Promise对象,代表后续操作的结果。
手写Promise的历程
第一步:实现基础Promise
手写Promise的第一步是实现一个基础的Promise类,它包含了resolve和reject两个方法。
class Promise {
constructor(executor) {
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (e) {
this.reject(e);
}
}
resolve(value) {
if (this.status !== 'pending') return;
this.status = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(callback => callback(value));
}
reject(reason) {
if (this.status !== 'pending') return;
this.status = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(callback => callback(reason));
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
const promise2 = new Promise((resolve, reject) => {
if (this.status === 'fulfilled') {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
} else if (this.status === 'rejected') {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
} else {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
reject(new TypeError('Chaining cycle detected for promise!'));
}
if (x instanceof Promise) {
x.then(y => resolvePromise(promise2, y, resolve, reject), reason => reject(reason));
} else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
const then = x.then;
if (typeof then === 'function') {
then.call(x, y => resolvePromise(promise2, y, resolve, reject), reason => reject(reason));
} else {
resolve(x);
}
} catch (e) {
reject(e);
}
} else {
resolve(x);
}
}
第二步:模拟链式调用
实现了基础Promise后,接下来需要模拟链式调用。链式调用的本质是将前一个操作的结果作为后一个操作的参数。
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功');
}, 1000);
});
promise1.then(result => {
console.log(result); // 输出:成功
});
第三步:遇到困难
当代码写到这一步时,我遇到了困难。如何处理多个then操作?如何保证then操作的顺序执行?如何处理异常情况?一系列的问题接踵而至。
我尝试了各种方法,但始终无法得到理想的结果。于是,我开始怀疑自己的能力,觉得自己或许根本不适合写代码。
请教高手
就在我濒临绝望之际,我决定向高手求助。我将代码发给了一位经验丰富的程序员朋友,请他指点一二。
朋友耐心地审阅了我的代码,指出其中的问题所在。原来,我在处理多个then操作和保证顺序执行方面存在缺陷。
在朋友的指导下,我修改了代码,终于实现了链式调用的功能。
结语
手写Promise的过程是一段艰辛的旅程。从一开始的信心满满到后来的怀疑自我,再到最后的恍然大悟,这一路走来,我经历了太多。
虽然最终成功手写了Promise,但我并没有因此而自满。我知道,我还有很多需要学习的。这次经历让我深刻地意识到,编程是一门需要不断学习和积累的学问。
对于那些和我一样还在学习编程的小伙伴,我只有一句忠告:不要害怕失败,不断学习,终有一天,你也会成为一名优秀的程序员。