Process Events & Signals
The process object is an EventEmitter, so you can subscribe to lifecycle moments and operating-system signals. This is how production services implement graceful shutdown, catch otherwise-fatal errors, and clean up resources before they vanish. Getting these handlers right is the difference between a deploy that drops live connections and one that drains them cleanly.
The lifecycle events
Event | Fires when | Async allowed? |
|---|---|---|
| The process is about to exit — last chance | No — sync only |
| The loop emptied with no pending work | Yes — you may schedule more |
| An error bubbled all the way up uncaught | Log & exit only |
| A promise rejected with no | Log & exit only |
| Node emits a runtime warning (e.g. leak) | Yes |
| Ctrl+C in the terminal | Yes |
| A polite "please stop" (Docker/K8s/ | Yes |
Graceful shutdown
When an orchestrator stops your container it sends SIGTERM, then waits a grace period (often 30s) before SIGKILL. A good service stops accepting new work, finishes in-flight requests, closes the DB, then exits — instead of dropping connections mid-flight:
graceful-shutdown.js
let shuttingDown = false
function shutdown(signal) {
if (shuttingDown) return // ignore a second Ctrl+C
shuttingDown = true
console.log(`Received ${signal}, shutting down gracefully...`)
server.close(() => { // 1. stop accepting new connections
db.close() // 2. release resources
console.log('Closed cleanly')
process.exit(0) // 3. success
})
// Safety net: force-exit if cleanup hangs past the grace period
setTimeout(() => {
console.error('Cleanup timed out, forcing exit')
process.exit(1)
}, 10_000).unref()
}
process.on('SIGTERM', () => shutdown('SIGTERM'))
process.on('SIGINT', () => shutdown('SIGINT'))Last-resort error handlers
process.on('uncaughtException', (err, origin) => {
console.error('Uncaught exception:', err, 'from', origin)
// Log, flush, then exit — the process is in an unknown state.
process.exit(1)
})
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled rejection at:', promise, 'reason:', reason)
process.exit(1)
})Common signals
Signal | Typical source | Catchable? |
|---|---|---|
| Ctrl+C at the terminal | Yes |
|
| Yes |
| Terminal closed; often used for "reload config" | Yes |
|
| No — instant death |
| Job control (pause) | No |
kill -SIGTERM 48213 # polite stop — your handler runs kill -SIGKILL 48213 # forceful — no handler, no cleanup, gone