使用 Promise.allSettled 和 try/catch 未处理的承诺拒绝

问题描述 投票:0回答:3

我的想法是这样的: 我想同时发送多个请求,而不必等到先前的执行。

所以我的伪代码如下所示:

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);
检查哪些请求有效,哪些请求失败。我希望这是清楚的。

javascript node.js promise try-catch
3个回答
6
投票

有趣的问题 - 问题是你实际上并没有模拟异步请求。事实上,您的两个请求方法只是创建同步/立即解决/拒绝的承诺。您需要将

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

正如预期的那样。


0
投票

知道为什么会发生这种情况吗?

您创建了

failingRequest()
,然后等待 4 秒再处理它。

我只插入睡眠,以便我可以测试

…因此你导致了未处理的拒绝。删除

await sleep(4000);
,它将按预期工作!


0
投票

你的代码首先创建一个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();
© www.soinside.com 2019 - 2024. All rights reserved.