我的想法是这样的: 我想同时发送多个请求,而不必等到先前的执行。
所以我的伪代码如下所示:
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function failingRequest(){
return new Promise((resolve, reject) => {
reject('Request failed');
});
}
function successRequest(){
return new Promise((resolve, reject) => {
resolve('Request success');
});
}
async function main() {
try {
let executions = [];
executions.push(failingRequest());
await sleep(4000);
executions.push(successRequest());
let result = await Promise.allSettled(executions);
console.log(result);
} catch (err) {
console.log('Outer error occured.');
console.log(err.message);
}
console.log('done');
}
main();
在此处运行此代码可以按预期在浏览器中运行,但会给出以下使用节点运行的输出:
node:761) UnhandledPromiseRejectionWarning: Request failed
api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:761) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exi not handled will terminate the Node.js process with a non-zero exit code.
[
{ status: 'rejected', reason: 'Request failed' },
{ status: 'fulfilled', value: 'Request success' }
]
done
(node:761) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
知道为什么会发生这种情况吗?
请注意,我只插入了
sleep
,这样我就可以测试 catch
块是否会被执行,以防第一个请求失败这不是所需的行为。我想同时发起这些请求,并且我不在乎是否有一个失败。我想稍后使用 let result = await Promise.allSettled(executions);
检查哪些请求有效,哪些请求失败。我希望这是清楚的。
有趣的问题 - 问题是你实际上并没有模拟异步请求。事实上,您的两个请求方法只是创建同步/立即解决/拒绝的承诺。您需要将
await
放在 failingRequest()
之前,以便被拒绝的 Promise 被捕获在周围的 try/catch
中,但这可能不是您想要的。
相反,你不应该立即“开始”承诺,而应该是这样的:
try {
let executions = [];
executions.push(failingRequest);
await sleep(4000);
executions.push(successRequest);
let result = await Promise.allSettled(executions.map(promiseFn => promiseFn()));
console.log(result);
} catch (err) {
console.log('Outer error occured.');
console.log(err.message);
}
这将记录
[
{ status: 'rejected', reason: 'Request failed' },
{ status: 'fulfilled', value: 'Request success' }
]
done
正如预期的那样。
知道为什么会发生这种情况吗?
您创建了
failingRequest()
,然后等待 4 秒再处理它。
我只插入睡眠,以便我可以测试
…因此你导致了未处理的拒绝。删除
await sleep(4000);
,它将按预期工作!
你的代码首先创建一个promise,通过调用
failingRequest
立即拒绝,这会导致JS将其推送到消息队列以供稍后处理。然后继续执行main
,当运行await sleep(4000)
时,它会将控制权返回给事件循环,以便JS可以在4秒后恢复它。现在,事件循环拉取并处理队列中等待的下一条消息,failingRequest
:JS 注意到它没有 .catch() 处理程序,抛出 UnhandledPromiseRejection 错误。
如果在创建 Promise 并将其添加到
Promise.allSettled
之间没有将控制权返回给事件循环,则 Promise.allSettled
会在从消息队列处理 failingRequest
消息之前首先运行,这允许它对其进行标记被拒绝了。
我在您的代码中添加了注释以使其更加清晰:
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function failingRequest() {
return new Promise((resolve, reject) => {
reject("Request failed");
});
}
function successRequest() {
return new Promise((resolve, reject) => {
resolve("Request success");
});
}
async function main() {
try {
let executions = [];
/*
* Executes the failingRequest function. Note that failingRequest returns a promise
* that rejects immediately *but* its "callback" is pushed as a message to
* JavaScript's message queue. The event loop will wait for the current function (main)
* to finish ["Run-to-completion"](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop#run-to-completion)
* before handling the failingRequest message from the queue and determining if it rejected.
*
* Note:
* To be more precise, the event loop doesn't wait for the main function to finish, but
* rather waits for the current synchronous code to finish. In this case, it's the code
* up to the await sleep(4000); line. After this line, the main function is paused, and
* the event loop can handle other tasks, including the rejected failingRequest promise.
*
* See also:
* - [Zero delays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop#zero_delays)
* - [Using Promises - Guarantees](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#guarantees)
*/
executions.push(failingRequest());
/**
* You could also confirm the promise is rejected here by doing:
*
* const failingPromise = failingRequest();
* console.log("failingPromise", failingPromise);
* // Logs: failingPromise Promise { <rejected> 'Request failed' }
* executions.push(failingPromise);
*/
/*
* The main function *awaits* another promise: this causes main to pause and return control
* to the event loop. Now the event loop will handle the failingRequest message that has
* been waiting in the message queue. JS notices it doesn't have a .catch() handler so it
* throws an UnhandledPromiseRejection error.
*/
await sleep(4000);
executions.push(successRequest());
/*
* Assuming the main function has not returned control to the event loop since the promises
* were created, Promise.allSettled waits for all promises in the executions array to either
* resolve or reject, and then resolves with an array of the results. In this example, even
* though failingRequest rejects, Promise.allSettled does not throw an UnhandledPromiseRejection error.
* Instead, it simply marks the status of failingRequest as "rejected" and continues on.
*/
let result = await Promise.allSettled(executions);
console.log(result);
} catch (err) {
console.log("Outer error occured.");
console.log(err.message);
}
console.log("done");
}
main();