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
.
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.
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.
')); }); 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.
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.
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);
}
})();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With