How to handle graceful shutdown with promises in Node.js applications

When building Node.js applications, it is important to handle graceful shutdowns properly to ensure that any ongoing operations are completed before the application terminates. One common approach to achieve this is by using promises.

Understanding graceful shutdown

Graceful shutdown refers to a process where the application takes steps to complete any pending operations or tasks before stopping. This is particularly important in long-running applications, such as servers, where there might be active connections, ongoing database transactions, or background jobs that need to be completed.

Using promises for graceful shutdown

Node.js provides an event called SIGINT which is emitted when the application receives a termination signal, such as when the user presses Ctrl + C in the terminal. We can leverage promises to handle the graceful shutdown process elegantly.

Here’s an example of how to handle graceful shutdown using promises in a Node.js application:

const { promisify } = require('util');
const { once } = require('events');

const sleep = promisify(setTimeout);
const terminationSignal = once(process, 'SIGINT');

async function shutdown() {
  console.log('Graceful shutdown initiated...');

  // Add code to gracefully close any connections or complete ongoing tasks.
  // For example, close database connections, cancel queued jobs, etc.

  console.log('Waiting for pending operations to complete...');
  await sleep(2000);

  console.log('Graceful shutdown completed.');
  process.exit(0);
}

(async () => {
  try {
    await terminationSignal;
    await shutdown();
  } catch (error) {
    console.error('Error occurred during shutdown:', error);
    process.exit(1);
  }
})();

In this example, we first import the promisify function from the util module to convert the setTimeout function into a promise-based function. This allows us to use await to pause the execution for a specified amount of time.

We then use the once function from the events module to create a promise that resolves when the SIGINT signal is received. This ensures that the shutdown process is initiated when the termination signal is received.

The shutdown function handles the actual graceful shutdown logic. Here, you can add code to close any open connections, cancel queued tasks, or complete ongoing operations. In this example, we simply wait for 2 seconds using the sleep function before exiting the application.

Finally, we wrap the shutdown process in an immediately invoked async function to handle any errors that may occur during the shutdown process. If an error occurs, we log it to the console and exit the application with a non-zero status code to indicate an error.

Conclusion

Handling graceful shutdowns in Node.js applications is crucial for ensuring that ongoing operations are completed before the application terminates. By leveraging promises, we can easily handle the shutdown process in an elegant and structured manner.