promise-toolbox
Advanced tools
Comparing version 0.9.5 to 0.10.0
{ | ||
"name": "promise-toolbox", | ||
"version": "0.9.5", | ||
"version": "0.10.0", | ||
"license": "ISC", | ||
@@ -25,7 +25,10 @@ "description": "Essential utils for promises", | ||
"preferGlobal": false, | ||
"main": "dist/", | ||
"bin": {}, | ||
"files": [ | ||
"dist/" | ||
"*.js", | ||
"*.js.map" | ||
], | ||
"browserslist": [ | ||
">2%" | ||
], | ||
"engines": { | ||
@@ -35,54 +38,41 @@ "node": ">=4" | ||
"dependencies": { | ||
"make-error": "^1.2.3" | ||
"make-error": "^1.3.2" | ||
}, | ||
"devDependencies": { | ||
"babel-cli": "^6.24.1", | ||
"babel-eslint": "^7.2.3", | ||
"babel-preset-env": "^1.5.1", | ||
"babel-preset-stage-0": "^6.24.1", | ||
"babili": "^0.1.2", | ||
"browserify": "^14.4.0", | ||
"cross-env": "^5.0.0", | ||
"dependency-check": "^2.8.0", | ||
"husky": "^0.13.4", | ||
"jest": "^20.0.4", | ||
"rimraf": "^2.6.1", | ||
"standard": "^10.0.2" | ||
"@babel/cli": "7.0.0", | ||
"@babel/core": "7.0.0", | ||
"@babel/plugin-proposal-function-bind": "7.0.0", | ||
"@babel/preset-env": "7.0.0", | ||
"babel-core": "^7.0.0-bridge.0", | ||
"babel-eslint": "^9.0.0", | ||
"babel-jest": "^23.2.0", | ||
"babel-minify": "^0.4.0", | ||
"babelify": "^8.0.0", | ||
"browserify": "^16.0.0", | ||
"cross-env": "^5.1.3", | ||
"eslint": "^5.0.1", | ||
"eslint-config-standard": "^12.0.0", | ||
"eslint-plugin-import": "^2.8.0", | ||
"eslint-plugin-node": "^7.0.1", | ||
"eslint-plugin-promise": "^4.0.0", | ||
"eslint-plugin-standard": "^4.0.0", | ||
"husky": "^0.14.3", | ||
"jest": "^23.2.0", | ||
"rimraf": "^2.6.2" | ||
}, | ||
"scripts": { | ||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/", | ||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=./ src/", | ||
"clean": "rimraf '*.js' '*.js.map'", | ||
"commitmsg": "npm test", | ||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/", | ||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=./ src/", | ||
"dev-test": "jest --bail --watch", | ||
"postbuild": "browserify -s promiseToolbox dist/index.js | babili > dist/umd.js", | ||
"posttest": "standard && dependency-check ./package.json", | ||
"prebuild": "rimraf dist/", | ||
"postbuild": "browserify -s promiseToolbox index.js | babel-minify > umd.js", | ||
"prebuild": "npm run clean", | ||
"predev": "npm run prebuild", | ||
"prepublish": "npm run build", | ||
"prepublishOnly": "npm run build", | ||
"pretest": "eslint --ignore-path .gitignore --fix src/", | ||
"test": "jest" | ||
}, | ||
"babel": { | ||
"env": { | ||
"test": { | ||
"ignore": null | ||
} | ||
}, | ||
"ignore": [ | ||
"fixtures.js", | ||
"*.spec.js" | ||
], | ||
"presets": [ | ||
[ | ||
"env", | ||
{ | ||
"targets": { | ||
"browsers": "> 5%", | ||
"node": 4 | ||
} | ||
} | ||
], | ||
"stage-0" | ||
] | ||
}, | ||
"jest": { | ||
"collectCoverage": true, | ||
"testEnvironment": "node", | ||
@@ -93,9 +83,3 @@ "roots": [ | ||
"testRegex": "\\.spec\\.js$" | ||
}, | ||
"standard": { | ||
"ignore": [ | ||
"dist" | ||
], | ||
"parser": "babel-eslint" | ||
} | ||
} |
352
README.md
@@ -5,39 +5,42 @@ # promise-toolbox [![Build Status](https://travis-ci.org/JsCommunity/promise-toolbox.png?branch=master)](https://travis-ci.org/JsCommunity/promise-toolbox) | ||
Features: | ||
**Features:** | ||
- compatible with all promise implementations | ||
- small (< 150 KB with all dependencies, < 5 KB with gzip) | ||
- nice with ES2015 / ES2016 syntax | ||
- Usage | ||
+ [Cancelation](#cancelation) | ||
- [Creation](#creation) | ||
- [Consumption](#consumption) | ||
- [Is cancel token?](#is-cancel-token) | ||
- [Combining cancel tokens](#combining-cancel-tokens) | ||
- [Forking cancel tokens](#forking-cancel-tokens) | ||
- [@cancelable decorator](#cancelable-decorator) | ||
+ [Resource management](#resource-management) | ||
+ [Functions](#functions) | ||
- [defer()](#defer) | ||
- [fromCallback(cb => fn(arg1, ..., argn, cb))](#fromcallbackcb--fnarg1--argn-cb) | ||
- [fromEvent(emitter, event, [options]) => Promise](#fromeventemitter-event-options--promise) | ||
- [fromEvents(emitter, successEvents, errorEvents) => Promise](#fromeventsemitter-successevents-errorevents--promise) | ||
- [isPromise(value)](#ispromisevalue) | ||
- [join(p1, ..., pn, cb) / join([p1, ..., pn], cb)](#joinp1--pn-cb--joinp1--pn-cb) | ||
- [promisify(fn, [ context ]) / promisifyAll(obj)](#promisifyfn--context---promisifyallobj) | ||
- [try(fn) / attempt(fn)](#tryfn--attemptfn) | ||
- [wrapApply(fn, args, [thisArg]) / wrapCall(fn, arg, [thisArg])](#wrapapplyfn-args-thisarg--wrapcallfn-arg-thisarg) | ||
+ [Pseudo-methods](#pseudo-methods) | ||
- [promises::all([ mapper ])](#promisesall-mapper-) | ||
- [promise::asCallback(cb)](#promiseascallbackcb) | ||
- [promise::catchPlus(predicate, cb)](#promisecatchpluspredicate-cb) | ||
- [promise::delay(ms)](#promisedelayms) | ||
- [collection::forEach(cb)](#collectionforeachcb) | ||
- [promise::ignoreErrors()](#promiseignoreerrors) | ||
- [promise::lastly(cb)](#promiselastlycb) | ||
- [promise::reflect()](#promisereflect) | ||
- [promises::some(count)](#promisessomecount) | ||
- [promise::tap(onResolved, onRejected)](#promisetaponresolved-onrejected) | ||
- [promise::timeout(ms, [cb])](#promisetimeoutms-cb) | ||
**Table of contents:** | ||
- [Cancelation](#cancelation) | ||
- [Creation](#creation) | ||
- [Consumption](#consumption) | ||
- [Is cancel token?](#is-cancel-token) | ||
- [@cancelable decorator](#cancelable-decorator) | ||
- [Resource management](#resource-management) | ||
- [Functions](#functions) | ||
- [asyncFn(generator)](#asyncfngenerator) | ||
- [asyncFn.cancelable(generator)](#asyncfncancelablegenerator) | ||
- [defer()](#defer) | ||
- [fromCallback(cb => fn(arg1, ..., argn, cb))](#fromcallbackcb--fnarg1--argn-cb) | ||
- [fromEvent(emitter, event, [options]) => Promise](#fromeventemitter-event-options--promise) | ||
- [fromEvents(emitter, successEvents, errorEvents) => Promise](#fromeventsemitter-successevents-errorevents--promise) | ||
- [isPromise(value)](#ispromisevalue) | ||
- [promisify(fn, [ context ]) / promisifyAll(obj)](#promisifyfn--context---promisifyallobj) | ||
- [try(fn)](#tryfn) | ||
- [wrapApply(fn, args, [thisArg]) / wrapCall(fn, arg, [thisArg])](#wrapapplyfn-args-thisarg--wrapcallfn-arg-thisarg) | ||
- [Pseudo-methods](#pseudo-methods) | ||
- [promises::all([ mapper ])](#promisesall-mapper-) | ||
- [promise::asCallback(cb)](#promiseascallbackcb) | ||
- [promise::catch(predicate, cb)](#promisecatchpredicate-cb) | ||
- [promise::delay(ms, [value])](#promisedelayms-value) | ||
- [collection::forEach(cb)](#collectionforeachcb) | ||
- [promise::ignoreErrors()](#promiseignoreerrors) | ||
- [promise::finally(cb)](#promisefinallycb) | ||
- [promise::reflect()](#promisereflect) | ||
- [promises::some(count)](#promisessomecount) | ||
- [promise::suppressUnhandledRejections()](#promisesuppressunhandledrejections) | ||
- [promise::tap(onResolved, onRejected)](#promisetaponresolved-onrejected) | ||
- [promise::tapCatch(onRejected)](#promisetapcatchonrejected) | ||
- [promise::timeout(ms, [cb])](#promisetimeoutms-cb-or-rejectionvalue) | ||
### Node & [Browserify](http://browserify.org/)/[Webpack](https://webpack.js.org/) | ||
@@ -61,2 +64,4 @@ | ||
### Promise support | ||
If your environment may not natively support promises, you should use a polyfill such as [native-promise-only](https://github.com/getify/native-promise-only). | ||
@@ -76,2 +81,37 @@ | ||
### Imports | ||
You can either import all the tools directly: | ||
```js | ||
import * as PT from 'promise-toolbox' | ||
console.log(PT.isPromise(value)) | ||
``` | ||
Or import individual tools from the main module: | ||
```js | ||
import { isPromise } from 'promise-toolbox' | ||
console.log(isPromise(value)) | ||
``` | ||
Each tool is also exported with a `p` prefix to work around reserved keywords | ||
and to help differentiate with other tools (like `lodash.map`): | ||
```js | ||
import { pCatch, pMap } from 'promise-toolbox' | ||
``` | ||
If you are bundling your application (Browserify, Rollup, Webpack, etc.), you | ||
can cherry-pick the tools directly: | ||
```js | ||
import isPromise from 'promise-toolbox/isPromise' | ||
import pCatch from 'promise-toolbox/catch' | ||
``` | ||
## API | ||
### Cancelation | ||
@@ -105,2 +145,11 @@ | ||
A list of existing tokens can be passed to `source()` to make the created token | ||
follow their cancelation: | ||
```js | ||
// `source.token` will be canceled (synchronously) as soon as `token1` or | ||
// `token2` or token3` is, with the same reason. | ||
const { cancel, token } = CancelToken.source([token1, token2, token3]) | ||
``` | ||
#### Consumption | ||
@@ -140,4 +189,4 @@ | ||
> Asynchronous handlers are executed on token cancelation and the | ||
promise returned by the `cancel` function will wait for all handlers | ||
to settle. | ||
> promise returned by the `cancel` function will wait for all handlers | ||
> to settle. | ||
@@ -161,3 +210,3 @@ ```js | ||
httpRequest(token, { | ||
hostname: 'example.org' | ||
hostname: 'example.org', | ||
}).then(response => { | ||
@@ -182,33 +231,2 @@ // do something with the response of the request | ||
#### Combining cancel tokens | ||
> Create a token which is canceled as soon as one token amongst many | ||
> is. | ||
```js | ||
// `token` will be canceled (synchronously) as soon as `token1` or | ||
// `token2` or token3` is, with the same reason. | ||
const token = CancelToken.race([ token1, token2, token3 ]) | ||
``` | ||
#### Forking cancel tokens | ||
> Create a new token which is canceled as soon as the original token | ||
> is or as soon as the executor decides. | ||
```js | ||
// token is canceled as soon as otherToken is or when the button is | ||
// clicked. | ||
const token = otherToken.fork(cancel => { | ||
$('#some-button').on('click', () => cancel('button clicked')) | ||
}) | ||
``` | ||
If no executor is passed, `#fork()` works like `.source()` and returns | ||
an object with a cancel function and a token: | ||
```js | ||
const { cancel, token } = otherToken.fork() | ||
``` | ||
#### @cancelable decorator | ||
@@ -279,2 +297,47 @@ | ||
#### asyncFn(generator) | ||
> Create an [async function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) from [a generator function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) | ||
> | ||
> Similar to [`Bluebird.coroutine`](http://bluebirdjs.com/docs/api/promise.coroutine.html). | ||
```js | ||
import { asyncFn } from 'promise-toolbox' | ||
const getUserName = asyncFn(function * (db, userId)) { | ||
const user = yield db.getRecord(userId) | ||
return user.name | ||
}) | ||
``` | ||
#### asyncFn.cancelable(generator) | ||
> Like [`asyncFn(generator)`](#asyncfngenerator) but the created async function supports [cancelation](#cancelation). | ||
> | ||
> Similar to [CAF](https://github.com/getify/CAF). | ||
```js | ||
import { asyncFn, CancelToken } from 'promise-toolbox' | ||
const getUserName = asyncFn.cancelable(function * (cancelToken, db, userId)) { | ||
// this yield will throw if the cancelToken is activated | ||
const user = yield db.getRecord(userId) | ||
return user.name | ||
}) | ||
const source = CancelToken.source() | ||
getUserName(source.token, db, userId).then( | ||
name => { | ||
console.log('user name is', name) | ||
}, | ||
error => { | ||
console.error(error) | ||
} | ||
) | ||
// only wait 5 seconds to fetch the user from the database | ||
setTimeout(source.cancel, 5e3) | ||
``` | ||
#### defer() | ||
@@ -303,6 +366,5 @@ | ||
fromCallback(cb => fs.readFile('foo.txt', cb)) | ||
.then(content => { | ||
console.log(content) | ||
}) | ||
fromCallback(cb => fs.readFile('foo.txt', cb)).then(content => { | ||
console.log(content) | ||
}) | ||
``` | ||
@@ -325,3 +387,3 @@ | ||
// name of the error event | ||
error: 'error' | ||
error: 'error', | ||
}) | ||
@@ -348,7 +410,3 @@ | ||
```js | ||
fromEvents( | ||
emitter, | ||
[ 'foo', 'bar' ], | ||
[ 'error1', 'error2' ] | ||
).then( | ||
fromEvents(emitter, ['foo', 'bar'], ['error1', 'error2']).then( | ||
values => { | ||
@@ -358,3 +416,7 @@ console.log('event %s have been emitted with values', values.event, values) | ||
reasons => { | ||
console.error('error event %s has been emitted with errors', reasons.event, reasons) | ||
console.error( | ||
'error event %s has been emitted with errors', | ||
reasons.event, | ||
reasons | ||
) | ||
} | ||
@@ -374,18 +436,5 @@ ) | ||
#### join(p1, ..., pn, cb) / join([p1, ..., pn], cb) | ||
> Easiest and most efficient way to wait for a fixed amount of | ||
> promises. | ||
```js | ||
import { join } from 'promise-toolbox' | ||
join(getPictures(), getComments(), getTweets(), (pictures, comments, tweets) => { | ||
console.log(`in total: ${pictures.length + comments.length + tweets.length}`) | ||
}) | ||
``` | ||
#### promisify(fn, [ context ]) / promisifyAll(obj) | ||
> Creates async functions taking node-style callbacks, create new ones | ||
> Creates async functions taking node-style callbacks, create new ones | ||
> returning promises. | ||
@@ -410,3 +459,3 @@ | ||
#### try(fn) / attempt(fn) | ||
#### try(fn) | ||
@@ -418,8 +467,9 @@ > Starts a chain of promises. | ||
const getUserById = id => PromiseToolbox.try(() => { | ||
if (typeof id !== 'number') { | ||
throw new Error('id must be a number') | ||
} | ||
return db.getUserById(id) | ||
}) | ||
const getUserById = id => | ||
PromiseToolbox.try(() => { | ||
if (typeof id !== 'number') { | ||
throw new Error('id must be a number') | ||
} | ||
return db.getUserById(id) | ||
}) | ||
``` | ||
@@ -455,6 +505,3 @@ | ||
```js | ||
const promises = [ | ||
Promise.resolve('foo'), | ||
Promise.resolve('bar') | ||
] | ||
const promises = [Promise.resolve('foo'), Promise.resolve('bar')] | ||
@@ -472,8 +519,5 @@ promises::all().then(values => { | ||
```js | ||
var promises = [ | ||
Promise.resolve('foo'), | ||
Promise.resolve('bar') | ||
] | ||
const promises = [Promise.resolve('foo'), Promise.resolve('bar')] | ||
all.call(promises).then(function (values) { | ||
all.call(promises).then(function(values) { | ||
console.log(values) | ||
@@ -528,3 +572,3 @@ }) | ||
#### promise::catchPlus(predicate, cb) | ||
#### promise::catch(predicate, cb) | ||
@@ -539,14 +583,18 @@ > Similar to `Promise#catch()` but: | ||
```js | ||
somePromise.then(() => { | ||
return a.b.c.d() | ||
})::catchPlus(TypeError, ReferenceError, reason => { | ||
// Will end up here on programmer error | ||
})::catchPlus(NetworkError, TimeoutError, reason => { | ||
// Will end up here on expected everyday network errors | ||
})::catchPlus(reason => { | ||
// Catch any unexpected errors | ||
}) | ||
somePromise | ||
.then(() => { | ||
return a.b.c.d() | ||
}) | ||
::pCatch(TypeError, ReferenceError, reason => { | ||
// Will end up here on programmer error | ||
}) | ||
::pCatch(NetworkError, TimeoutError, reason => { | ||
// Will end up here on expected everyday network errors | ||
}) | ||
::pCatch(reason => { | ||
// Catch any unexpected errors | ||
}) | ||
``` | ||
#### promise::delay(ms) | ||
#### promise::delay(ms, [value]) | ||
@@ -565,3 +613,3 @@ > Delays the resolution of a promise by `ms` milliseconds. | ||
```js | ||
console.log(await delay.call('500ms passed', 500)) | ||
console.log(await delay(500, '500ms passed')) | ||
// → 500 ms passed | ||
@@ -580,6 +628,3 @@ ``` | ||
```js | ||
[ | ||
'foo', | ||
Promise.resolve('bar'), | ||
]::forEach(value => { | ||
['foo', Promise.resolve('bar')]::forEach(value => { | ||
console.log(value) | ||
@@ -604,13 +649,17 @@ | ||
// exist | ||
readFileAsync('foo.txt').then(content => { | ||
console.log(content) | ||
})::ignoreErrors() | ||
readFileAsync('foo.txt') | ||
.then(content => { | ||
console.log(content) | ||
}) | ||
::ignoreErrors() | ||
// will emit an unhandled rejection error due to the typo | ||
readFileAsync('foo.txt').then(content => { | ||
console.lgo(content) // typo | ||
})::ignoreErrors() | ||
readFileAsync('foo.txt') | ||
.then(content => { | ||
console.lgo(content) // typo | ||
}) | ||
::ignoreErrors() | ||
``` | ||
#### promise::lastly(cb) | ||
#### promise::finally(cb) | ||
@@ -624,7 +673,7 @@ > Execute a handler regardless of the promise fate. Similar to the | ||
```js | ||
import { lastly } from 'promise-toolbox' | ||
import { pFinally } from 'promise-toolbox' | ||
function ajaxGetAsync (url) { | ||
return new Promise((resolve, reject) => { | ||
const xhr = new XMLHttpRequest | ||
const xhr = new XMLHttpRequest() | ||
xhr.addEventListener('error', reject) | ||
@@ -634,3 +683,3 @@ xhr.addEventListener('load', resolve) | ||
xhr.send(null) | ||
})::lastly(() => { | ||
})::pFinally(() => { | ||
$('#ajax-loader-animation').hide() | ||
@@ -665,10 +714,26 @@ }) | ||
const [ first, seconds ] = await [ | ||
const [first, seconds] = await [ | ||
ping('ns1.example.org'), | ||
ping('ns2.example.org'), | ||
ping('ns3.example.org'), | ||
ping('ns4.example.org') | ||
ping('ns4.example.org'), | ||
]::some(2) | ||
``` | ||
#### promise::suppressUnhandledRejections() | ||
> Suppress unhandled rejections, needed when error handlers are attached | ||
> asynchronously after the promise has rejected. | ||
> | ||
> Similar to [`Bluebird#suppressUnhandledRejections()`](http://bluebirdjs.com/docs/api/suppressunhandledrejections.html). | ||
```js | ||
const promise = getUser()::suppressUnhandledRejections() | ||
$(document).on('ready', () => { | ||
promise.catch(error => { | ||
console.error('error while getting user', error) | ||
}) | ||
}) | ||
``` | ||
#### promise::tap(onResolved, onRejected) | ||
@@ -678,3 +743,3 @@ | ||
> | ||
> Like `::lastly()`, if the callback rejects, it takes over the | ||
> Like `::finally()`, if the callback rejects, it takes over the | ||
> original resolution/rejection. | ||
@@ -697,4 +762,8 @@ | ||
#### promise::timeout(ms, [cb]) | ||
#### promise::tapCatch(onRejected) | ||
> Alias to [`promise:tap(null, onRejected)`](#promisetaponresolved-onrejected). | ||
#### promise::timeout(ms, [cb or rejectionValue]) | ||
> Call a callback if the promise is still pending after `ms` | ||
@@ -714,2 +783,7 @@ > milliseconds. Its resolution/rejection is forwarded. | ||
await doLongOperation()::timeout(100) | ||
await doLongOperation()::timeout( | ||
100, | ||
new Error('the long operation has failed') | ||
) | ||
``` | ||
@@ -732,3 +806,3 @@ | ||
# Build for production (automatically called by npm install) | ||
# Build for production | ||
> npm run build | ||
@@ -739,3 +813,3 @@ ``` | ||
Contributions are *very* welcomed, either on the documentation or on | ||
Contributions are _very_ welcomed, either on the documentation or on | ||
the code. | ||
@@ -742,0 +816,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
160241
97
1169
796
20
3
1
Updatedmake-error@^1.3.2