Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Catch Promise rejection at a later time [duplicate]

How do I retrieve the result of a promise at a later time? In a test, I am retrieving an email before sending further requests:

const email = await get_email();
assert.equal(email.subject, 'foobar');
await send_request1();
await send_request2();

How can I send the requests while the slow email retrieval is going on?

At first, I considered awaiting the email later:

// This code is wrong - do not copy!
const email_promise = get_email();
await send_request1();
await send_request2();
const email = await email_promise;
assert.equal(email.subject, 'foobar');

This works if get_email() is successful, but fails if get_email() fails before the corresponding await, with a completely justified UnhandledPromiseRejectionWarning.

Of course, I could use Promise.all, like this:

await Promise.all([
    async () => {
        const email = await get_email();
        assert.equal(email.subject, 'foobar');
    },
    async () => {
        await send_request1();
        await send_request2();
    },
]);

However, it makes the code much harder to read (it looks more like callback-based programming), especially if later requests actually depend on the email, or there is some nesting going on. Is it possible to store the result/exception of a promise and await it at a later time?

If need be, here is a testcase with mocks that sometimes fail and sometimes work, with random timings. It must never output UnhandledPromiseRejectionWarning.

like image 914
phihag Avatar asked Dec 20 '18 13:12

phihag


People also ask

Can a Promise be rejected multiple times?

It is not safe to resolve/reject promise multiple times. It is basically a bug, that is hard to catch, becasue it can be not always reproducible.

Does rejecting Promise go to catch?

Normally, such . catch doesn't trigger at all. But if any of the promises above rejects (a network problem or invalid json or whatever), then it would catch it.

How many times can a Promise be rejected?

')); }); An important point to note: A Promise executor should call only one resolve or one reject . Once one state is changed (pending => fulfilled or pending => rejected), that's all.

How do you handle Promise rejection warning?

If an error condition arises inside a promise, you “reject” the promise by calling the reject() function with an error. To handle a promise rejection, you pass a callback to the catch() function.


1 Answers

const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const send_request1 = () => wait(300), send_request2 = () => wait(200);
async function get_email() {
    await wait(Math.random() * 1000);
    if (Math.random() > 0.5) throw new Error('failure');
    return {subject: 'foobar'};
}

const assert = require('assert');
async function main() {
    // catch possible error
    const email_promise = get_email().catch(e => e);
    await send_request1();
    await send_request2();
    // wait for result
    const email = await email_promise;
    // rethrow eventual error or do whatever you want with it
    if(email instanceof Error) {
      throw email;
    }
    assert.equal(email.subject, 'foobar');
};

(async () => {
    try {
        await main();
    } catch(e) {
        console.log('main error: ' + e.stack);
    }
})();
like image 71
ponury-kostek Avatar answered Nov 11 '22 09:11

ponury-kostek