好的,这是一个非常核心且有深度的 JavaScript 问题。我们不仅要实现这些功能,更重要的是理解其背后的逻辑和原理。
我会按照以下顺序为你手写实现并解释:
-
Promise.all:等待所有 Promise 都成功。 -
Promise.race:返回第一个成功或失败的 Promise 的结果。 -
Promise.any:返回第一个成功的 Promise 的结果。 -
Promise.allSettled:等待所有 Promise 完成(无论成功或失败)。 -
async/await的模拟实现:通过 Generator 函数来理解其本质。
1. 手写实现 Promise.all
核心逻辑
Promise.all(promises) 接收一个 Promise 数组,并返回一个新的 Promise。
-
成功:当且仅当
promises数组中所有的 Promise 都成功(fulfilled)时,返回的 Promise 才会成功,并且其成功的值是一个包含了所有 Promise 成功值的数组,顺序与原数组一致。 -
失败:只要
promises数组中有一个 Promise 失败(rejected),返回的 Promise 就会立即失败,并且其失败的原因是第一个失败的 Promise 的原因。 -
空数组:如果传入的数组为空,则立即成功,并返回一个空数组。
实现
JavaScript
function myPromiseAll(promises) {
return new Promise((resolve, reject) => {
// 确保输入是可迭代的
if (!promises || typeof promises[Symbol.iterator] !== 'function') {
return reject(new TypeError('Argument is not iterable'));
}
const promisesArray = Array.from(promises);
const results = [];
let completedCount = 0;
const numPromises = promisesArray.length;
// 处理空数组的边界情况
if (numPromises === 0) {
return resolve([]);
}
promisesArray.forEach((promise, index) => {
// 使用 Promise.resolve() 来处理非 Promise 的值
Promise.resolve(promise)
.then(value => {
// 保证结果的顺序与输入一致
results[index] = value;
completedCount++;
// 当所有 promise 都成功时,resolve 结果数组
if (completedCount === numPromises) {
resolve(results);
}
})
.catch(error => {
// 任何一个 promise 失败,整个 myPromiseAll 就失败
reject(error);
});
});
});
}
// --- 使用示例 ---
const p1 = Promise.resolve(1);
const p2 = new Promise(resolve => setTimeout(() => resolve(2), 100));
const p3 = Promise.resolve(3);
const p4_reject = Promise.reject('Error occurred');
myPromiseAll([p1, p2, p3])
.then(values => console.log('Promise.all success:', values)) // 输出: Promise.all success: [1, 2, 3]
.catch(error => console.error('Promise.all failed:', error));
myPromiseAll([p1, p2, p4_reject])
.then(values => console.log('Promise.all success:', values))
.catch(error => console.error('Promise.all failed:', error)); // 输出: Promise.all failed: Error occurred
2. 手写实现 Promise.race
核心逻辑
Promise.race(promises) 接收一个 Promise 数组,并返回一个新的 Promise。
- 返回的这个新 Promise 的状态会与
promises数组中第一个“尘埃落定”(settled,即成功或失败)的 Promise 的状态保持一致。谁快,就用谁的结果。
实现
JavaScript
function myPromiseRace(promises) {
return new Promise((resolve, reject) => {
if (!promises || typeof promises[Symbol.iterator] !== 'function') {
return reject(new TypeError('Argument is not iterable'));
}
const promisesArray = Array.from(promises);
// 如果是空数组,Promise 将永远 pending,符合规范
if (promisesArray.length === 0) {
return;
}
promisesArray.forEach(promise => {
Promise.resolve(promise)
.then(value => {
// 第一个成功的 promise 会 resolve 外层的 promise
resolve(value);
})
.catch(error => {
// 第一个失败的 promise 会 reject 外层的 promise
reject(error);
});
});
});
}
// --- 使用示例 ---
const p_fast = new Promise(resolve => setTimeout(() => resolve('fast'), 50));
const p_slow = new Promise(resolve => setTimeout(() => resolve('slow'), 200));
const p_reject_fast = new Promise((_, reject) => setTimeout(() => reject('fast reject'), 100));
myPromiseRace([p_fast, p_slow])
.then(value => console.log('Promise.race success:', value)) // 输出: Promise.race success: fast
.catch(error => console.error('Promise.race failed:', error));
myPromiseRace([p_slow, p_reject_fast])
.then(value => console.log('Promise.race success:', value))
.catch(error => console.error('Promise.race failed:', error)); // 输出: Promise.race failed: fast reject
3. 手写实现 Promise.any
核心逻辑
Promise.any(promises) 接收一个 Promise 数组,并返回一个新的 Promise。
-
成功:只要
promises数组中有一个 Promise 成功,返回的 Promise 就会立即成功,并且其成功的值是第一个成功的 Promise 的值。 -
失败:当且仅当
promises数组中所有的 Promise 都失败时,返回的 Promise 才会失败,并且其失败的原因是一个AggregateError对象,它包含了所有 Promise 的失败原因。
实现
JavaScript
function myPromiseAny(promises) {
return new Promise((resolve, reject) => {
if (!promises || typeof promises[Symbol.iterator] !== 'function') {
return reject(new TypeError('Argument is not iterable'));
}
const promisesArray = Array.from(promises);
const numPromises = promisesArray.length;
let errors = [];
let rejectedCount = 0;
if (numPromises === 0) {
return reject(new AggregateError([], 'All promises were rejected'));
}
promisesArray.forEach((promise, index) => {
Promise.resolve(promise)
.then(value => {
// 只要有一个成功,就立即 resolve
resolve(value);
})
.catch(error => {
errors[index] = error;
rejectedCount++;
// 当所有 promise 都失败时,才 reject
if (rejectedCount === numPromises) {
reject(new AggregateError(errors, 'All promises were rejected'));
}
});
});
});
}
// --- 使用示例 ---
const p_any_reject1 = Promise.reject('any reject 1');
const p_any_reject2 = Promise.reject('any reject 2');
const p_any_resolve = new Promise(resolve => setTimeout(() => resolve('any resolve'), 100));
myPromiseAny([p_any_reject1, p_any_resolve, p_any_reject2])
.then(value => console.log('Promise.any success:', value)) // 输出: Promise.any success: any resolve
.catch(error => console.error('Promise.any failed:', error));
myPromiseAny([p_any_reject1, p_any_reject2])
.then(value => console.log('Promise.any success:', value))
.catch(error => {
console.error('Promise.any failed:', error.message); // 输出: Promise.any failed: All promises were rejected
console.error('Errors:', error.errors); // 输出: Errors: ['any reject 1', 'any reject 2']
});
4. 手写实现 Promise.allSettled
核心逻辑
Promise.allSettled(promises) 接收一个 Promise 数组,并返回一个新的 Promise。
-
这个新的 Promise 永远不会失败。
-
它会等待所有
promises数组中的 Promise 都“尘埃落定”(settled)后,才会成功。 -
其成功的值是一个数组,每个元素都描述了对应 Promise 的结果,格式为
{status: 'fulfilled', value: ...}或{status: 'rejected', reason: ...}。
实现
JavaScript
function myPromiseAllSettled(promises) {
return new Promise((resolve) => {
if (!promises || typeof promises[Symbol.iterator] !== 'function') {
// allSettled 的规范实际上是返回一个被拒绝的Promise
return Promise.reject(new TypeError('Argument is not iterable'));
}
const promisesArray = Array.from(promises);
const results = [];
let settledCount = 0;
const numPromises = promisesArray.length;
if (numPromises === 0) {
return resolve([]);
}
promisesArray.forEach((promise, index) => {
Promise.resolve(promise)
.then(value => {
results[index] = { status: 'fulfilled', value: value };
})
.catch(reason => {
results[index] = { status: 'rejected', reason: reason };
})
.finally(() => {
settledCount++;
if (settledCount === numPromises) {
resolve(results);
}
});
});
});
}
// --- 使用示例 ---
const p_settled_resolve = Promise.resolve('settled resolve');
const p_settled_reject = Promise.reject('settled reject');
const p_settled_pending = new Promise(r => setTimeout(() => r('settled pending done'), 100));
myPromiseAllSettled([p_settled_resolve, p_settled_reject, p_settled_pending])
.then(results => console.log('Promise.allSettled results:', JSON.stringify(results, null, 2)));
/*
输出:
Promise.allSettled results: [
{
"status": "fulfilled",
"value": "settled resolve"
},
{
"status": "rejected",
"reason": "settled reject"
},
{
"status": "fulfilled",
"value": "settled pending done"
}
]
*/
5. async/await 的模拟实现
核心逻辑
首先要明确,async/await 是 ECMAScript 的语言级特性,我们无法在 JavaScript 代码中“实现”关键字。但我们可以通过编写一个函数来模拟它的行为,从而深刻理解其运行原理。
async/await 本质上是 Generator 函数 和 Promise 的语法糖。
-
async function就像一个特殊的 Generator 函数。 -
await关键字会暂停async function的执行,就像 Generator 函数中的yield。 -
JavaScript 引擎在背后有一个“执行器”(Executor),它会自动执行 Generator,处理
yield后面跟的 Promise,并在 Promise 完成后将结果传回 Generator 继续执行。
我们的目标就是编写这个“执行器”函数。
实现
JavaScript
// 这个是执行器函数,它接收一个 generator 函数作为参数
function asyncRunner(generatorFunc) {
// 返回一个新的函数,这个函数被调用时会返回一个Promise
return function(...args) {
return new Promise((resolve, reject) => {
// 1. 创建并启动 generator
const generator = generatorFunc(...args);
// 2. 定义一个 step 函数,用于驱动 generator 执行
function step(nextFn) {
let generatorResult;
try {
// 3. 调用 generator 的 next/throw 方法
generatorResult = nextFn();
} catch (error) {
// 如果 generator 内部出错,直接 reject
return reject(error);
}
const { value, done } = generatorResult;
if (done) {
// 4. 如果 generator 执行完毕,用最终结果 resolve Promise
return resolve(value);
}
// 5. 如果 generator 未执行完毕,确保 value 是一个 Promise
Promise.resolve(value)
.then(
// 6a. 如果 value (Promise) 成功,将成功的值传回 generator 继续执行
res => {
step(() => generator.next(res));
},
// 6b. 如果 value (Promise) 失败,将错误抛入 generator
err => {
step(() => generator.throw(err));
}
);
}
// 7. 第一次启动执行
step(() => generator.next(undefined));
});
};
}
// --- 使用示例 ---
// 模拟一个异步获取数据的函数
function fetchData(data) {
return new Promise(resolve => setTimeout(() => resolve(`${data} - done`), 200));
}
// 这是一个 Generator 函数,它的写法和 async/await 非常相似
function* myAsyncLogic() {
try {
const result1 = yield fetchData('data1');
console.log(result1); // 输出: data1 - done
const result2 = yield fetchData('data2');
console.log(result2); // 输出: data2 - done
return `All finished: ${result1}, ${result2}`;
} catch (e) {
console.error('Caught error in generator:', e);
}
}
// 使用我们的执行器来运行这个 generator
const myAsyncFunction = asyncRunner(myAsyncLogic);
myAsyncFunction()
.then(finalResult => {
console.log(finalResult); // 输出: All finished: data1 - done, data2 - done
});
async/await 实现小结
这个 asyncRunner 函数清晰地揭示了 async/await 的工作流:
-
调用
async函数,实际上是创建了一个 Generator 迭代器。 -
await一个 Promise,相当于yield这个 Promise,函数执行权交还给执行器。 -
执行器等待这个 Promise 完成。
-
Promise 成功,执行器调用
iterator.next(resolvedValue),将结果作为await表达式的值,恢复函数执行。 -
Promise 失败,执行器调用
iterator.throw(error),将错误抛入函数内部,可以被try...catch捕获。 -
这个过程递归地进行,直到 Generator 执行完毕(
done: true),此时最外层的 Promise 被 resolve。