How to Fire and Forget a Promise in Nodejs (Es7)

Can I fire and forget a promise in nodejs (ES7)?

Yes, you can do that, and it will run the two asynchronous functions in parallel. You've just created a promise and thrown it away.

However, this means that when the promise is rejected you won't notice. You'll just get an unhandledRejection eventually which will crash your process if not handled.

Is this OK? How can I run something that I don't care?

Probably it's not OK. If you truly wouldn't care, you hadn't run it in the first place. So you should be clear and explicit what you care about (and what not):

  • do you want to wait? (for side effects)
  • do you need the result?
  • do you want to catch exceptions?

If you only want to wait and don't care for the result value, you can easily throw away the result:

void (await someAsyncFunction()); // or omit the void keyword,
// doesn't make a difference in an expression statement

If you don't care about exceptions, you can ignore them using

… someAsyncFunction().catch(function ignore() {}) …

You can throw that away, await it, do anything with it.

If you want the result, you have to await it. If you care about exceptions, but don't really want to wait, you may want to execute it in parallel with the following functions:

var [_, res] = await Promise.all([
someAsyncFunction(), // result is ignored, exceptions aren't
someOtherAsyncFunction()
]);
return res;

Does node.js allow promises to be run without any kind of await/expected fulfillment?

There is no requirement in Javascript to await a promise. If you want to make an asynchronous call that returns a promise and ignore when it completes, you can do that just fine. That is sometimes referred to as "fire and forget". I often do this with closing files, for example since I have nothing useful to do when the file is finally closed.

There is a requirement to catch any rejection from a promise that can happen. So, if your promise has any chance of being rejected, you will need at least use a .catch() or a try/catch around an await.

In this particular example:

client.on('message', async message => {
await message.member.edit({mute: true})
});

There is no requirement to use await there and, in fact, it does nothing useful at all. It doesn't stop or pause anything. If message.member.edit({mute: true}) returns a promise, then the await would suspend execution of that callback and immediately return a promise back to the event system that triggered the event, but other events in nodejs will continue to get processed and that's the last line in the function anyway, so nothing is actually accomplished with the await.

If message.member.edit({mute: true}) returns a promise and you want to wait for all of them to be done before you do something else in your callback, then you will have to either process them serially with a for loop or process them in parallel and track them with Promise.all():

// process serially with for loop and await  
client.on('message', async message => {
try {
let memberArray = [message.member];
for (let member of memberArray) {
await member.edit({mute: true});
}
console.log("Do something after promised mute is completed");
} catch(e) {
// catch any rejections
console.log(e);
}
});

// process in parallel with Promise.all()
client.on('message', async message => {
try {
let memberArray = [message.member];
await Promise.all(memberArray.map(member => member.edit({mute: true})));
console.log("Do something after promised mute is completed");
} catch(e) {
// catch any rejections
console.log(e);
}
});

Now, if member.edit({mute: true}) is not asynchronous and returning a promise linked to the asynchronous operation, then all of this is moot as no await is needed or useful in any circumstance.

Can I use fs promise API without then() or await?

No, you dont have to do anything with the returned promise, just like every non-async function it will be executed even if you ignore the return value, but there are some things you have to keep in mind:

if you dont wait for the promise to resolve or reject, the following actions, such as your res.send('Hello World'); most likely will be executed before the async operation is done. It depends on your application if this is a problem or not.

You also cant handle possible exceptions that might occur within the async function if you dont have a catch block, therefore your whole application might crash when running into an unhandled exception.

Are there any (negative) side effects when I don't use the then function of a Promise?

Remember that every call to then results in a new promise. So any Promise implementation that had a memory leak as a result of a promise not having any resolution handlers attached would be a broken implementation, what with all those promises that we never hook handlers to being returned. I very much doubt implementations of ES2015's promises, Bluebird, Q, etc. have that behavior.

Separately, as conceptually a promise's resolution handlers are basically just functions stored by the promise and then called when appropriate, it's not likely to be a memory leak if you never give it any functions to store.

But, there is a problem with your fire-and-forget, just not a memory leak problem: It breaks one of the main Promise rules: Either handle rejection, or return the promise chain to something else that will handle rejection. Since you're not doing that, if the operation fails, you'll have an unhandled rejection. Unhandled rejections are reported to the console and in some environments may terminate your app (at some point, Node.js may start terminating the process when this happens, see this open issue).

If the fact that then returns a new promise is a surprise, consider:

let p1 = new Promise(resolve => {  setTimeout(() => {    resolve('a');  }, 100);});let p2 = p1.then(result => {  console.log("p1.then got " + result);  return 'b';});p2.then(result => {  console.log("p2.then got " + result);});

Is there a way to throw an error from a non-awaited promise in NodeJs?

No. This is exactly why you don't do that.

You have to either await it:

async function willTimeout() {
try {
await somePromise
} catch (e) {
// Handle error gracefully here
}
}

or use .then()/.catch():

function willTimeout() {
return somePromise.catch(e => {
// Handle error gracefully here
});
}

I am trying to write a NodeJS script that will return gracefully if it takes too long.

For that, see Timeout in async/await:

async function willTimeout() {
try {
return await Promise.race([
new Promise((resolve, reject) => {
setTimeout(reject, 1, new Error("timeout"));
}),
variableLengthFunction()
]);
} catch(err) {
// Handle error from `variableLengthFunction` or timeout gracefully here
}
}

Return instant response without waiting for async function

In an async function, await waits for a promise to settle before allowing the function logic to continue. If you don't want to wait for a promise to settle, don't use await on it.

So for instance, if the operation you want to start but not wait for is the page.goto call, remove the await in front of it. You might also add a rejection handler via .catch so you can handle and report any error it returns (asynchronously, after your function has returned):

//  v−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− no `await`
page.goto('https://www.website.com', {
waitUntil: 'networkidle0',
})
.catch(error => {
// Handle/report the fact an error occurred; at this point, your
// `createOrder` call has already finished and sent the response, so
// you can't send an error response but you can use the information
// in some other way
});

void before promise syntax

void is an operator that accepts a value on the Right-Hand Side and evaluates as undefined.

It resolves no-floating-promises because it does something (or rather, explicitly nothing) with the promise.

What are standard JS practices about when it's OK *not* to await for an async task?

Whether and when to do this will be a matter of opinion, so that part is off-topic for Stack Overflow.

But the concrete part is the question of: if you do this, are there any precautions you have to take? And the answer there is: Yes. You need to catch and handle errors.

Consider this fire-and-forget async call that returns a promise:

doSomethingAsync();

If the thing being done can fail, that promise can be rejected. If it's rejected and nothing handles that rejection, then:

  • In a browser, you get an error written to the browser console. 99.9999999% of users won't notice.
  • In Node.js, you get this error written to the console:

(node:26477) [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 exit code.

Note that warning: At some point, a future version of Node.js may start *terminating the process when you allow a promise rejection to go unhandled.

So if you fire-and-forget, be sure to catch errors, even if you just silently swallow them:

doSomethingAsync().catch(error => {}); // Obviously only do this if you really don't
// care about the error!

Fun side note: In JavaScript, you have unhandled promise fulfillment all the time. Consider:

doSomethingAsync()
.then(result => {
// ...do something with result...
})
.catch(error => {
// ...handle/report the error...
});

Are all the promises handled there?

No. And that's just fine.

Remember that then and catch create and return a promise. The promise returned by catch in the above has no handlers attached to it. That's fine, provided it is only ever fulfilled, not rejected. Nothing ever does anything with that fulfillment. :-)

Avoiding Floating Promises in TypeScript. Void vs .catch When? Why?

Adding void here doesn't really alter the behavior of your program, so the only reason this could be mentioned is as a sort of 'shut up' operator, e.g.: a means to quiet your linter.



Related Topics



Leave a reply



Submit