@unboundedsystems/node-graceful
Version:
Graceful process exit manager. allows waiting on multiple async services.
246 lines (186 loc) • 7.3 kB
Markdown
# node-graceful
[](https://travis-ci.org/mrbar42/node-graceful) [](https://www.npmjs.com/package/node-graceful) 
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.