Konubinix' site

Does Nodejs Wait for All Promises Before Exiting?

Fleeting

Between each run of the event loop, Node.js checks if it is waiting for any asynchronous I/O or timers and shuts down cleanly if there are not any.

https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick

nodejs waits for lower level stuff than promises1. Fortunately, promises are almost always due to the need to deal with those.

But in theory, you might have an unresolved promise when the program quits.

That is difficult to simulate, as you need to make this promise “wait” before being run so that node has the time to exit. And the only way to explicitly wait is to use a timer, that nodejs will wait to resolve2.

You can maybe craft something using .unref()3

You can for instance, create a promise with a timer that does not make nodejs wait for it.

const delay = ms => new Promise(res => setTimeout(res, ms).unref());

And then start this promise.

delay(time).then(() => console.log("I fulfilled my promise!"))
console.log(`I promised I would wait for ${time}`)

In order to realize that nodejs can still wait, let’s add an artificial wait timer.

setTimeout(() => {console.log('done with it');}, 1000)

In the end, the program will wait for one second and totally ignore the promise created with the call of delay.

I promised I would wait for 3000
done with it

As you can see, the promise is never fulfilled.

You can try setting a shorter value to see this promise resolve when node has the time to wait for it.

You should see something like this:

I promised I would wait for 500
I fulfilled my promise!
done with it

Not that obvious to reproduce

You can try having an async function that always wait for another. Both don’t use either asynchronous operations or timers.

async function outer ()
{
   while(true)
   {
       console.log("calling inner")
       await inner()
   }
}
async function inner ()
{
   console.log("in inner")
}

outer()

In my experience, this code never ends, while I suppose that it should at some time.

I assume that it is good to keep in mind the fact that nodejs does not make the promise (pun intended) to wait for promises, but in real life, it happens in very specific occasions.

In by chance I get into this trap in the future in real life, I might add this real life example in here.


  1. if you did just this:

    const myPromise = new Promise((resolve, reject) => { console.log(“did nothing here”); });

    Then, nodejs would not wait for that promise all by itself, even though that promise never resolves or rejects. This is because nodejs does not actually wait for promises. It waits for the underlying asynchronous operations that are usually behind promises. Since there is no such underlying asynchronous operation behind this promise, nodejs does not wait for it.

    https://stackoverflow.com/questions/66860218/why-does-node-seemingly-wait-for-all-promises-to-resolve

     ↩︎
  2. Node event loop keeps running until all outstanding tasks are completed or cancelled.

    setTimeout creates a pending event, so the loop will keep running until that executes.

    Outstanding Promises, setInterval and other mechanisms can all prevent the event loop from halting.

    It’s worth noting that setTimeout does not use a promise at all. That’s just a regular callback function. The setTimeout() API long predates Promises

    https://stackoverflow.com/questions/66860218/why-does-node-seemingly-wait-for-all-promises-to-resolve

     ↩︎
  3. worth mentioning that you can actually tell nodejs to NOT wait for some asynchronous operations by using the .unref() method. In the example you show, if you do this:

    const timer = setTimeout(() => {console.log(‘hello’);}, 5000); timer.unref();

    https://stackoverflow.com/questions/66860218/why-does-node-seemingly-wait-for-all-promises-to-resolve

     ↩︎