手写简化版 Promise 详解
在JavaScript中,Promise
是一种用于处理异步操作的强大机制。虽然现代JavaScript环境(如Node.js和浏览器)已经内置了功能完备的 Promise
实现,但了解如何手写一个简化版的 Promise
可以帮助我们深入理解其内部工作原理。
一、Promise 的基本结构
一个基本的 Promise
实现需要包含几个关键部分:状态管理、执行器(executor)函数、以及处理成功和失败的回调函数的队列。
1. 状态管理
Promise
有三种状态:pending
(等待中)、fulfilled
(已成功)、rejected
(已失败)。状态一旦改变就不能再变。
class MyPromise {
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 (error) {
this._reject(error);
}
}
_resolve(value) {
if (this.status === 'pending') {
this.status = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn(value));
}
}
_reject(reason) {
if (this.status === 'pending') {
this.status = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn(reason));
}
}
}
2. 执行器函数
执行器函数是传递给 Promise
构造函数的参数,它接受两个函数作为参数:resolve
和 reject
。这两个函数用于改变 Promise
的状态。
二、then 方法
then
方法是 Promise
的核心,它允许我们为 Promise
成功或失败时注册回调函数。
MyPromise.prototype.then = function(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
if (this.status === 'fulfilled') {
setTimeout(() => onFulfilled(this.value));
} else if (this.status === 'rejected') {
setTimeout(() => onRejected(this.reason));
} else {
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
}
// 链式调用和错误传递的实现(简化版)
return new MyPromise((resolve, reject) => {
if (this.status === 'fulfilled') {
setTimeout(() => {
try {
const result = onFulfilled(this.value);
// 这里简化处理,假设result不是Promise
resolve(result);
} catch (error) {
reject(error);
}
});
} else if (this.status === 'rejected') {
// 类似地处理rejected状态
}
});
};
注意:上面的 then
方法实现是简化的,它只处理了 fulfilled
状态下的情况,并且没有处理 onFulfilled
或 onRejected
返回 Promise
的情况。在完整的实现中,你需要添加对这些情况的处理。
三、静态方法和链式调用
为了支持 Promise
链式调用和错误传递,then
方法需要返回一个新的 Promise
。此外,Promise
还有一些静态方法,如 Promise.all
和 Promise.race
,这些方法的实现需要额外的逻辑来跟踪和管理多个 Promise
的状态。
四、优化与完整实现
在完整的 Promise
实现中,你需要考虑更多的边界情况和优化。例如:
- 处理
then
方法中onFulfilled
或onRejected
抛出异常的情况。 - 支持
then
方法返回Promise
的情况,这通常通过递归调用then
方法来实现。 - 实现
Promise.all
和Promise.race
等静态方法。 - 优化性能,例如通过减少不必要的回调函数调用次数。
手写一个完整的 Promise
实现是一个复杂但非常有教育意义的过程,因为它涉及到异步编程、状态管理、以及错误处理等多个方面。下面是一个更加完整但简化的 Promise
实现示例,这个实现将包括基本的状态管理、then
方法(支持返回 Promise
的情况)、以及链式调用和错误传递的支持。
class MyPromise {
constructor(executor) {
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.status === 'pending') {
this.status = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(fn => {
this._handleNextPromise(fn, value);
});
}
};
const reject = (reason) => {
if (this.status === 'pending') {
this.status = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => {
this._handleNextPromise(fn, reason);
});
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
_handleNextPromise(callback, valueOrReason) {
let returnValue;
try {
returnValue = callback(valueOrReason);
} catch (error) {
return this._rejectPromise(error);
}
if (returnValue instanceof MyPromise) {
returnValue.then(
this._resolvePromise.bind(this),
this._rejectPromise.bind(this)
);
} else {
this._resolvePromise(returnValue);
}
}
_resolvePromise(value) {
// 在这里简化处理,不考虑循环引用等情况
this._resolve(value);
}
_rejectPromise(reason) {
this._reject(reason);
}
_resolve(value) {
// 简化处理,不再重复
}
_reject(reason) {
// 简化处理,不再重复
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
return new MyPromise((resolve, reject) => {
if (this.status === 'fulfilled') {
setTimeout(() => {
try {
this._handleNextPromise(onFulfilled, this.value);
} catch (error) {
reject(error);
}
});
} else if (this.status === 'rejected') {
setTimeout(() => {
try {
this._handleNextPromise(onRejected, this.reason);
} catch (error) {
reject(error);
}
});
} else {
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
}
});
}
// 这里可以添加更多方法,如 catch, finally, static 方法等
}
new MyPromise((resolve, reject) => {
setTimeout(() => resolve(42), 1000);
}).then(value => {
console.log(value); // 输出 42
return new MyPromise((resolve, reject) => {
setTimeout(() => resolve(value * 2), 500);
});
}).then(value => {
console.log(value); // 输出 84
});
注意:
- 这个实现仍然有许多简化和未处理的情况,比如循环引用的检测、
Promise
规范化(即处理then
方法返回的不是Promise
的情况)等。 - 在
_handleNextPromise
方法中,我们尝试执行回调函数,并根据返回值进行进一步的处理。如果回调函数返回了一个MyPromise
实例,我们将该实例的then
方法与内部的resolve
和reject
方法绑定,以实现链式调用。 setTimeout
的使用是为了模拟异步操作,它确保then
方法中的回调函数在下一个事件循环中执行,从而模拟真实的异步行为。
在实际应用中,Promise
的实现会更加复杂,特别是要处理各种边界情况和优化性能。不过,基于我们之前的简化实现,我们可以继续添加一些重要的功能,比如 catch
方法和 finally
方法,以及处理 Promise
静态方法如 Promise.all
、Promise.race
、Promise.resolve
和 Promise.reject
。
下面是一个扩展了 catch
和 finally
方法的 MyPromise
实现:
class MyPromise {
// ... (之前的代码保持不变)
catch(onRejected) {
return this.then(null, onRejected);
}
finally(onFinally) {
return this.then(
value => MyPromise.resolve(onFinally()).then(() => value),
reason => MyPromise.reject(onFinally()).then(() => { throw reason; })
);
}
// 静态方法
static resolve(value) {
return new MyPromise(resolve => resolve(value));
}
static reject(reason) {
return new MyPromise((_, reject) => reject(reason));
}
// 注意:这里不实现 Promise.all, Promise.race 等,因为它们需要额外的逻辑来处理多个 Promise
}
// 使用示例
MyPromise.resolve(1)
.then(x => x + 1)
.then(x => {
if (x < 5) {
throw new Error('x is not greater than or equal to 5');
}
return x;
})
.catch(err => console.error('Caught an error:', err))
.finally(() => console.log('Finally block executed'));
// 输出:
// Caught an error: Error: x is not greater than or equal to 5
// Finally block executed
在这个扩展中,catch
方法简单地调用了 then
方法,并传递了 null
作为第一个参数(即不处理 fulfilled
状态),只传递了错误处理函数作为第二个参数。
finally
方法稍微复杂一些,它接受一个回调函数 onFinally
,该函数会在 Promise
结束时(无论是 fulfilled
还是 rejected
)执行。finally
方法需要确保原始 Promise
的结果(值或错误)被正确传递。这里,我们使用了 MyPromise.resolve(onFinally()).then(...)
来确保 onFinally
函数被异步执行,并且其结果不会影响原始 Promise
的值或错误。如果 onFinally
函数返回了一个 Promise
,我们需要等待它解决后再继续传递原始 Promise
的结果。
请注意,这里的 MyPromise.resolve(onFinally()).then(...)
假设 onFinally
函数不会抛出错误,或者即使它抛出错误,我们也希望忽略它并继续执行原始 Promise
的后续处理。在实际应用中,你可能需要添加额外的错误处理逻辑来确保 finally
块中的错误被适当地捕获和处理。
最后,MyPromise.resolve
和 MyPromise.reject
是静态方法,用于快速创建已解决或已拒绝的 Promise
对象。这些方法在实际应用中非常有用,因为它们允许你以编程方式创建 Promise
对象,而无需定义 executor
函数。
当然,我们可以继续扩展MyPromise
类,以下是一个简单的Promise.all
和Promise.race
的模拟实现,添加到我们的MyPromise
类中:
class MyPromise {
// ... (之前的代码保持不变,包括then, catch, finally, resolve, reject)
// 静态方法 Promise.all
static all(promises) {
return new MyPromise((resolve, reject) => {
let results = [];
let remaining = promises.length;
if (remaining === 0) {
resolve(results);
return;
}
promises.forEach((p, index) => {
MyPromise.resolve(p).then(
value => {
results[index] = value;
if (--remaining === 0) {
resolve(results);
}
},
err => {
reject(err);
}
);
});
});
}
// 静态方法 Promise.race
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(p => {
MyPromise.resolve(p).then(resolve, reject);
});
});
}
// ... (其他代码)
}
// 使用示例
MyPromise.all([
MyPromise.resolve(1),
MyPromise.resolve(2).then(x => x * 2),
MyPromise.reject(new Error('Failed')),
MyPromise.resolve(4)
]).then(values => console.log(values))
.catch(err => console.error('Failed:', err));
// 输出: Failed: Error: Failed
MyPromise.race([
MyPromise.resolve(1).delay(5000), // 注意:这里假设delay是一个自定义扩展,实际Promise没有
MyPromise.resolve(2)
]).then(value => console.log(value));
// 输出: 2
// 注意:由于JavaScript原生的Promise没有delay方法,这里只是一个示例。
// 在实际使用中,你可能需要使用setTimeout或其他方式来实现延迟。
注意:上面的delay
方法并不是Promise
原生的一部分,我只是用它来模拟一个长时间运行的Promise
。
MyPromise.all
方法等待所有给定的Promise
都成功解决,并将它们的值作为一个数组返回。如果任何一个Promise
被拒绝,那么MyPromise.all
立即以相同的错误拒绝。
MyPromise.race
方法返回一个新的Promise
,该Promise
在所有给定的Promise
中解决得最快的一个解决时解决,其解决值与那个最快的Promise
的解决值相同。如果任何一个Promise
被拒绝,返回的Promise
也立即以相同的错误被拒绝。
这些扩展使得MyPromise
类更加强大和灵活,能够处理更复杂的异步逻辑。然而,请注意,为了完全模拟原生Promise
的行为,还需要考虑更多的边界情况和优化。
结论:
通过实现MyPromise
类及其扩展功能,我们不仅加深了对Promise
工作原理的理解,还学会了如何以编程方式创建和管理异步操作。虽然MyPromise
类在功能上可能不如原生Promise
完整,但它为学习异步编程和Promise
模式提供了一个很好的起点。随着对JavaScript和异步编程的进一步学习,你将能够更深入地理解和应用这些概念。
本文由mdnice多平台发布
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。