Socket
Socket
Sign inDemoInstall

make-promises-safe

Package Overview
Dependencies
0
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.0.0 to 3.0.0

example-multiple-resolves.js

31

make-promises-safe.js
'use strict'
const event = 'unhandledRejection'
function install (event, cb) {
if (process.listenerCount(event) !== 0) {
return
}
if (process.listenerCount(event) === 0) {
setup()
process.on(event, cb)
}
function setup () {
process.on(event, function (err) {
console.error(err)
if (module.exports.abort) {
process.abort()
}
process.exit(1)
})
install('unhandledRejection', function (err) {
console.error(err)
abortOrExit()
})
install('multipleResolves', function (type, promise, reason) {
console.error(type, promise, reason)
abortOrExit()
})
function abortOrExit () {
if (module.exports.abort) {
process.abort()
}
process.exit(1)
}
module.exports.abort = false
{
"name": "make-promises-safe",
"version": "2.0.0",
"version": "3.0.0",
"description": "Crash or abort if you get an unhandledRejection or multipleResolves",

@@ -29,2 +29,3 @@ "main": "make-promises-safe.js",

"pre-commit": "^1.2.2",
"semver": "^5.6.0",
"snazzy": "^7.0.0",

@@ -31,0 +32,0 @@ "standard": "^10.0.2",

# make-promises-safe   [![Build Status](https://travis-ci.org/mcollina/make-promises-safe.svg?branch=master)](https://travis-ci.org/mcollina/make-promises-safe)
A node.js module to make the use of promises safe.
It implements the deprecation
[DEP0018][unhandled] of Node.js in versions 6, 7 and 8.
Using Promises without this module might cause file descriptor and memory
leaks.
It implements:
## The Problem
1. the deprecation [DEP0018][unhandled] of Node.js in versions >= 6, as
using Promises without this module might cause file descriptor and memory
leak; follow [this](#the-unhandledrejection-problem) for more details.
2. exiting on multiple resolve or reject, as those could hide bugs that
will be completely silent; follow
[this](#promises-that-resolves-or-rejects-multiple-times) for more details.
Node.js crashes if there is an uncaught exception, while it does not
crash if there is an `'unhandledRejection'`, i.e. a Promise without a
`.catch()` handler.
## Install
**If you are using promises, you should attach a `.catch()` handler
synchronously**.
```
npm install make-promises-safe --save
```
As an example, the following server will leak a file descriptor because
of a missing `.catch()`  handler:
## Usage
```js
'use strict'
require('make-promises-safe') // installs an 'unhandledRejection' handler
const http = require('http')

@@ -43,20 +46,33 @@ const server = http.createServer(handle)

## The Solution
### as a preloader
`make-promises-safe` installs an `process.on('unhandledRejection')`
handler that prints the stacktrace and exits the process with an exit
code of 1, just like any uncaught exception.
You can add this behavior to any Node.js application by using it as a
preloader:
## Install
```
node -r make-promises-safe server.js
```
### with core dumps
You can also create a core dump when an unhandled rejection occurs:
```
npm install make-promises-safe --save
require('make-promises-safe').abort = true
```
## Usage
<a name="unhandledRejection"></a>
## The 'unhandledRejection' problem
Node.js crashes if there is an uncaught exception, while it does not
crash if there is an `'unhandledRejection'`, i.e. a Promise without a
`.catch()` handler.
**If you are using promises, you should attach a `.catch()` handler
synchronously**.
As an example, the following server will leak a file descriptor because
of a missing `.catch()`  handler:
```js
'use strict'
require('make-promises-safe') // installs an 'unhandledRejection' handler
const http = require('http')

@@ -83,19 +99,66 @@ const server = http.createServer(handle)

### as a preloader
### The Solution
You can add this behavior to any Node.js application by using it as a
preloader:
`make-promises-safe` installs an [`process.on('unhandledRejection')`][unhandled-event]
handler that prints the stacktrace and exits the process with an exit
code of 1, just like any uncaught exception.
<a name="multipleResolves"></a>
## Promises that resolves or rejects multiple times
Promises whose `resolve` or `reject` functions are called multiple times
might cause memory or file descriptor leaks, as resource might not be
consumed correctly.
As an example, the following code will error with a EMFILE:
```js
const http = require('http')
const fs = require('fs')
const stream = require('stream')
const { promisify } = require('util')
const pipeline = promisify(stream.pipeline)
const server = http.createServer(handle)
server.listen(3000)
function handle (req, res) {
openFile()
.then((body) => {
return pipeline(body, res)
})
.catch((err) => {
res.statusCode = 500
res.end(err.toString())
})
}
function openFile () {
return new Promise(function (resolve, reject) {
// this simulates some other parts of the codebase that
// could throw an error
if (Math.random() < 0.5) {
// note that a deveveloper forgot to return
reject(new Error('kaboom'))
}
// we are creating a file even if the promise errored,
// and it is never consumed or closed
resolve(fs.createReadStream(__filename))
})
}
```
node -r make-promises-safe server.js
```
### with core dumps
Under load, the above script will exits with an EMFILE error that would
be extremely hard to debug.
You can also create a core dump when an unhandled rejection occurs:
### The Solution
`make-promises-safe` installs an [`process.on('multipleResolves')`][multiple-event]
handler that prints the type of event, the promise and the reason
(the value that `resolve` or `reject` were called with)
and exits the process with an exit code of 1.
```
require('make-promises-safe').abort = true
```
This feature is available only on Node >= 10.12.0.
## License

@@ -106,1 +169,3 @@

[unhandled]: https://nodejs.org/dist/latest-v8.x/docs/api/deprecations.html#deprecations_dep0018_unhandled_promise_rejections
[unhandled-event]: https://nodejs.org/dist/latest-v10.x/docs/api/process.html#process_event_unhandledrejection
[multiple-event]: https://nodejs.org/dist/latest-v10.x/docs/api/process.html#process_event_multipleresolves

@@ -6,3 +6,6 @@ 'use strict'

const spawn = require('child_process').spawn
const semver = require('semver')
const hasMultipleResolves = semver.satisfies(process.version, '>= 10.12.0')
const main = path.join(__dirname, require('./package').main)

@@ -27,1 +30,34 @@

})
test('crashes the process on double resolve', { skip: !hasMultipleResolves }, (t) => {
t.plan(2)
const child = spawn(process.execPath, [
'-r', main, '-e', 'new Promise((r) => { r(); r(\'a\') })'])
child.stderr.on('data', function (chunk) {
const expected = 'resolve Promise { undefined } a'
t.ok(chunk.toString().trim().indexOf(expected.trim()) === 0)
})
child.on('close', (code) => {
t.is(code, 1)
})
})
test('crashes the process on reject after resolve', { skip: !hasMultipleResolves }, (t) => {
t.plan(2)
const child = spawn(process.execPath, [
'-r', main, '-e', 'new Promise((resolve, reject) => { resolve(); reject(new Error(\'kaboom\')) })'])
child.stderr.on('data', function (chunk) {
const expected = `reject Promise { undefined } Error: kaboom
at Promise ([eval]:1:54)`
t.ok(chunk.toString().trim().indexOf(expected.trim()) === 0)
})
child.on('close', (code) => {
t.is(code, 1)
})
})

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc