UNPKG

@unboundedsystems/node-graceful

Version:

Graceful process exit manager. allows waiting on multiple async services.

246 lines (186 loc) 7.3 kB
# node-graceful [![Build Status](https://travis-ci.org/mrbar42/node-graceful.svg?branch=master)](https://travis-ci.org/mrbar42/node-graceful) [![npm](https://img.shields.io/npm/v/node-graceful.svg)](https://www.npmjs.com/package/node-graceful) ![npm bundle size (version)](https://img.shields.io/bundlephobia/minzip/node-graceful?label=Install%20size) node-graceful is a small helper module without dependencies that aims to ease graceful exit of complex node programs including async waiting on multiple independent modules. Installation: ```sh npm i -S node-graceful yarn add node-graceful ``` Had any problem? open an [issue](https://github.com/mrbar42/node-graceful/issues/new) ## Quick example ```javascript const Graceful = require('node-graceful'); Graceful.captureExceptions = true; Graceful.on('exit', async () => { console.log(`Received ${signal} - Exiting gracefully`); await webServer.close(); }); // Graceful will wait until all listeners had finished Graceful.on('exit', (signal) => { return new Promise((resolve) => { console.log("Another independent listener!"); setTimeout(() => resolve(), 1000); }); }); ``` Typescript ```typescript import Graceful from 'node-graceful'; Graceful.captureExceptions = true; Graceful.on('exit', async () => { await server.close(); }); ``` ## Quick Docs ```typescript interface Graceful { // add exit listener on(signal: 'exit', listener: GracefulListener): GracefulSubscription; // remove exit listener off(signal: 'exit', listener: GracefulListener): void; // remove all exit listeners clear(): void; // trigger graceful process exit with or without exit code and signal exit(): void; exit(exitCode: number): void; exit(exitSignal: string): void; exit(exitCode: number, exitSignal: string): void; // whether to exit immediately when a second kill signal is received exitOnDouble: boolean; // default: true // maximum time to wait before hard-killing the process timeout: number; // default: 30000 // whether to treat uncaught exceptions as process terminating events captureExceptions: boolean; // default: false // whether to treat unhandled promise rejections as process terminating events captureRejections: boolean; // default: false } type GracefulListener = (signal: string, details?: object) => (void | any | Promise<any> | Promise<Error>); type GracefulSubscription = () => void; ``` Read bellow for full API reference. ## API Reference ### Graceful.on('exit', {Function} listener) Add exit listener to be called when process exit is triggered. `Graceful` listens on all terminating signals and triggers `exit` accordingly. Terminating events: `SIGTERM` `SIGINT` `SIGBREAK` `SIGHUP` #### Options - `listener(signal, details?)` - listener function - `signal` - the signal that triggered the exit. example: 'SIGTERM' - `details` - optional details provided by the trigger. for example in case of `unhandledException` this will be an error object. on external signal it will be undefined. #### Examples The listener function can return a promise that will delay the process exit until it's fulfilment. ```typescript Graceful.on('exit', () => Promise.resolve('I Am A Promise!')); Graceful.on('exit', async () => { // async function always returns promise so shutdown will be delayed until this functions ends await webServer.close(); return Promise.all([ controller.close(), dbClient.close() ]); }); ``` if old style callback is needed, wrap the logic with a promise ```javascript const server = require('http').createServer(function (req, res) { res.write('ok'); res.end() }) Graceful.on('exit', () => { return new Promise((resolve, reject) => { server.close((err) => { if (err) return reject(err); resolve(); }); }); }); ``` #### Return value the method returns a function that when invoked, removes the listener subscription. the function is a shorthand for `.off` method ##### example ```typescript // use the return value to remove listener const removeListener = Graceful.on('exit', () => {}); removeListener(); // listener was removed and will not be triggered ``` ### Graceful.off('exit', {Function} listener) Remove a previously subscribed listener. ##### example ```typescript const gracefulExit = () => { console.log("exiting!"); }; // add listener let removeListener = Graceful.on('SIGTERM', gracefulExit); // remove listener Graceful.off('SIGTERM', gracefulExit); // same as invoking the return value // removeListener(); ``` ### Graceful.clear() Unsubscribe all `exit` listeners. ##### example ```javascript // add listener Graceful.on('exit', () => { console.log("Received some exit signal!"); return Promise.resolve("A promise to be waited on before dying"); }); Graceful.on('exit', (done) => { console.log("Another listener"); done(); }); // remove all listener Graceful.clear(); ``` ### Graceful.exit({Number} \[code], {String} \[signal]) Trigger graceful process exit. This method is meant to be a substitute command for `process.exit()` to allow other modules to exit gracefully in case of error. - `code` - (optional) exit code to be used. default - `process.exitCode` - `signal` - (optional) signal to be simulating for listeners. default - `SIGTERM` ##### example ```javascript server.listen(3333) .on('listening', function () { console.log('Yay!') }) .on('error', function (err) { if (err.code === 'EADDRINUSE') { console.error("Damn, Port is already in use..."); Graceful.exit(); } }); // exit code and signal can be specified // Graceful.exit(1); // Graceful.exit(1, 'SIGINT'); // Graceful.exit('SIGINT'); ``` ## Options Options are global and shared, any change will override previous values. #### Graceful.exitOnDouble = true {boolean} Whether to exit immediately when a second deadly event is received, For example when Ctrl-C is pressed twice etc.. When exiting due to double event, exit code will be `process.exitCode` or `1` (necessarily a non-zero) #### Graceful.timeout = 30000 {number} Maximum time to wait for exit listeners in `ms`. After exceeding the time, the process will force exit and the exit code will be `process.exitCode` or `1` (necessarily a non-zero) Setting the timeout to `0` will disable timeout functionality (will wait indefinitely) #### Graceful.captureExceptions = false {boolean} Whether to treat `uncaughtException` event as a terminating event and trigger graceful shutdown. ```typescript Graceful.captureExceptions = true; throw new Error('DANG!'); // this will now trigger graceful shutdown ``` #### Graceful.captureExceptions = false {boolean} Whether to treat `unhandledRejection` event as a terminating event and trigger graceful shutdown. On newer `node` versions `unhandledRejection` is in-fact a terminating event ```typescript Graceful.captureRejections = true; Promise.reject(new Error('DANG!')); // this will now trigger graceful shutdown ``` #### exitCode `Graceful` will obey `process.exitCode` property value when exiting unless the exit is forced (double signal, timeout) in which case the exit code must be non-zero.