Gathering detailed insights and metrics for promise-toolbox
Gathering detailed insights and metrics for promise-toolbox
Gathering detailed insights and metrics for promise-toolbox
Gathering detailed insights and metrics for promise-toolbox
dynamodb-toolbox
Lightweight and type-safe query builder for DynamoDB and TypeScript.
@ampproject/toolbox-core
Commonly used functionality for amp-toolbox
@nomicfoundation/hardhat-toolbox
Nomic Foundation's recommended bundle of Hardhat plugins (ethers based)
@metaplex-foundation/mpl-toolbox
Auto-generated essential Solana and Metaplex programs
npm install promise-toolbox
Module System
Min. Node Version
Typescript Support
Node Version
NPM Version
10 Stars
320 Commits
1 Forks
2 Watching
10 Branches
3 Contributors
Updated on 17 Oct 2022
Minified
Minified + Gzipped
JavaScript (100%)
Cumulative downloads
Total Downloads
Last day
-20.8%
18,749
Compared to previous day
Last week
-14.3%
101,532
Compared to previous week
Last month
18.8%
431,258
Compared to previous month
Last year
60.5%
3,688,161
Compared to previous year
1
Essential utils for promises.
Features:
Table of contents:
Installation of the npm package:
> npm install --save promise-toolbox
You can directly use the build provided at unpkg.com:
1<script src="https://unpkg.com/promise-toolbox@0.8/dist/umd.js"></script>
If your environment may not natively support promises, you should use a polyfill such as native-promise-only.
On Node, if you want to use a specific promise implementation, Bluebird for instance to have better performance, you can override the global Promise variable:
1global.Promise = require("bluebird");
Note that it should only be done at the application level, never in a library!
You can either import all the tools directly:
1import * as PT from "promise-toolbox";
2
3console.log(PT.isPromise(value));
Or import individual tools from the main module:
1import { isPromise } from "promise-toolbox"; 2 3console.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
):
1import { pCatch, pMap } from "promise-toolbox";
If you are bundling your application (Browserify, Rollup, Webpack, etc.), you can cherry-pick the tools directly:
1import isPromise from "promise-toolbox/isPromise"; 2import pCatch from "promise-toolbox/catch";
This library provides an implementation of CancelToken
from the
cancelable promises specification.
A cancel token is an object which can be passed to asynchronous functions to represent cancelation state.
1import { CancelToken } from "promise-toolbox";
A cancel token is created by the initiator of the async work and its cancelation state may be requested at any time.
1// Create a token which requests cancelation when a button is clicked. 2const token = new CancelToken((cancel) => { 3 $("#some-button").on("click", () => cancel("button clicked")); 4});
1const { cancel, token } = CancelToken.source();
A list of existing tokens can be passed to source()
to make the created token
follow their cancelation:
1// `source.token` will be canceled (synchronously) as soon as `token1` or 2// `token2` or token3` is, with the same reason. 3const { cancel, token } = CancelToken.source([token1, token2, token3]);
The receiver of the token (the function doing the async work) can:
1// 1. 2if (token.reason) { 3 console.log("cancelation has been requested", token.reason.message); 4} 5 6// 2. 7try { 8 token.throwIfRequested(); 9} catch (reason) { 10 console.log("cancelation has been requested", reason.message); 11} 12 13// 3. 14token.promise.then((reason) => { 15 console.log("cancelation has been requested", reason.message); 16}); 17 18// 4. 19subtask(token);
See asyncFn.cancelable
for an easy way to create async functions with built-in cancelation support.
Asynchronous handlers are executed on token cancelation and the promise returned by the
cancel
function will wait for all handlers to settle.
1function httpRequest(cancelToken, opts) {
2 const req = http.request(opts);
3 req.end();
4 cancelToken.addHandler(() => {
5 req.abort();
6
7 // waits for the socket to really close for the cancelation to be
8 // complete
9 return fromEvent(req, "close");
10 });
11 return fromEvent(req, "response");
12}
13
14const { cancel, token } = CancelToken.source();
15
16httpRequest(token, {
17 hostname: "example.org",
18}).then((response) => {
19 // do something with the response of the request
20});
21
22// wraps with Promise.resolve() because cancel only returns a promise
23// if a handler has returned a promise
24Promise.resolve(cancel()).then(() => {
25 // the request has been properly canceled
26});
1if (CancelToken.isCancelToken(value)) { 2 console.log("value is a cancel token"); 3}
This is deprecated, instead explicitely pass a cancel token or an abort signal:
1const asyncFunction = async (a, b, { cancelToken = CancelToken.none } = {}) => { 2 cancelToken.promise.then(() => { 3 // do stuff regarding the cancelation request. 4 }); 5 6 // do other stuff. 7};
Make your async functions cancelable.
If the first argument passed to the cancelable function is not a
cancel token, a new one is created and injected and the returned
promise will have a cancel()
method.
1import { cancelable, CancelToken } from "promise-toolbox";
2
3const asyncFunction = cancelable(async ($cancelToken, a, b) => {
4 $cancelToken.promise.then(() => {
5 // do stuff regarding the cancelation request.
6 });
7
8 // do other stuff.
9});
10
11// Either a cancel token is passed:
12const source = CancelToken.source();
13const promise1 = asyncFunction(source.token, "foo", "bar");
14source.cancel("reason");
15
16// Or the returned promise will have a cancel() method:
17const promise2 = asyncFunction("foo", "bar");
18promise2.cancel("reason");
If the function is a method of a class or an object, you can use
cancelable
as a decorator:
1class MyClass {
2 @cancelable
3 async asyncMethod($cancelToken, a, b) {
4 // ...
5 }
6}
A cancel token can be created from an abort signal:
1const token = CancelToken.from(abortSignal);
If
abortSignal
is already aCancelToken
, it will be returned directly, making it a breeze to create code accepting both :-)
A cancel token is API compatible with an abort signal and can be used as such:
1const { cancel, token } = CancelToken.source(); 2 3await fetch(url, { signal: token });
See Bluebird documentation for a good explanation.
A disposable is a simple object, which contains a dispose method and possibily a value:
1const disposable = { dispose: () => db.close(), value: db };
The dispose method may be asynchronous and return a promise.
As a convenience, you can use the Disposable
class:
1import { Disposable } from "promise-toolbox"; 2 3const disposable = new Disposable(() => db.close(), db);
If the process is more complicated, maybe because this disposable depends on
other disposables, you can use a generator function alongside the
Disposable.factory
decorator:
1const getTable = Disposable.factory(async function* () { 2 // simply yield a disposable to use it 3 const db = yield getDb(); 4 5 const table = await db.getTable(); 6 try { 7 // yield the value to expose it 8 yield table; 9 } finally { 10 // this is where you can dispose of the resource 11 await table.close(); 12 } 13});
Independent disposables can be acquired and disposed in parallel, to achieve
this, you can use Disposable.all
:
1const combined = await Disposable.all([disposable1, disposable2]);
Similarly to Promise.all
, the value of such a disposable, is an array whose
values are the values of the disposables combined.
To ensure all resources are properly disposed of, disposables must never be
used manually, but via the Disposable.use
function:
1import { Disposable } from "promise-toolbox";
2
3await Disposable.use(
4 // Don't await the promise here, resource acquisition should be handled by
5 // `Disposable.use` otherwise, in case of failure, other resources may failed
6 // to be disposed of.
7 getTable(),
8
9 // If the function can throw synchronously, a wrapper function can be passed
10 // directly to `Disposable.use`.
11 () => getTable(),
12
13 async (table1, table2) => {
14 // do something with table1 and table 2
15 //
16 // both `table1` and `table2` are guaranteed to be deallocated by the time
17 // the promise returned by `Disposable.use` is settled
18 }
19);
For more complex use cases, just like Disposable.factory
, the handler can be
a generator function when no disposables are passed:
1await Disposable.use(async function* () { 2 const table1 = yield getTable(); 3 const table2 = yield getTable(); 4 5 // do something with table1 and table 2 6 // 7 // both `table1` and `table2` are guaranteed to be deallocated by the time 8 // the promise returned by `Disposable.use` is settled 9});
To enable an entire function to use disposables, you can use Disposable.wrap
:
1// context (`this`) and all arguments are forwarded from the call
2const disposableUser = Disposable.wrap(async function* (arg1, arg2) {
3 const table = yield getDisposable(arg1, arg2);
4
5 // do something with table
6});
7
8// similar to
9function disposableUser(arg1, arg2) {
10 return Disposable.use(async function* (arg1, arg2) {
11 const table = yield getDisposable(arg1, arg2);
12
13 // do something with table
14 });
15}
Create an async function from a generator function
Similar to
Bluebird.coroutine
.
1import { asyncFn } from 'promise-toolbox' 2 3const getUserName = asyncFn(function * (db, userId)) { 4 const user = yield db.getRecord(userId) 5 return user.name 6})
Like
asyncFn(generator)
but the created async function supports cancelation.Similar to CAF.
1import { asyncFn, CancelToken } from 'promise-toolbox' 2 3const getUserName = asyncFn.cancelable(function * (cancelToken, db, userId)) { 4 // this yield will throw if the cancelToken is activated 5 const user = yield db.getRecord(userId) 6 return user.name 7}) 8 9const source = CancelToken.source() 10 11getUserName(source.token, db, userId).then( 12 name => { 13 console.log('user name is', name) 14 }, 15 error => { 16 console.error(error) 17 } 18) 19 20// only wait 5 seconds to fetch the user from the database 21setTimeout(source.cancel, 5e3)
1const cancelableAsyncFunction = asyncFn.cancelable(function* (
2 cancelToken,
3 ...args
4) {
5 // await otherAsyncFunction() but will throw if cancelToken is activated
6 yield otherAsyncFunction();
7
8 // if aborting on cancelation is unwanted (eg because the called function
9 // already handles cancelation), wrap the promise in an array
10 yield [otherAsyncFunction(cancelToken)];
11
12 // cancelation, just like any rejection, can be catch
13 try {
14 yield otherAsyncFunction();
15 } catch (error) {
16 if (CancelToken.isCancelToken(error)) {
17 // do some clean-up here
18 // the rest of the function has been added as an async handler of the
19 // CancelToken which will make `cancel` waits for it
20 }
21
22 throw error;
23 }
24
25 return result;
26});
If the cancel token is not the first param of the decorated function, a getter should be passed to asyncFn.cancelable
, it's called with the same context and arguments as the decorated function and returns the cancel token:
1const cancelableAsyncFunction = asyncFn.cancelable( 2 function*(arg1, arg2, options) { 3 // function logic 4 }, 5 (_arg1, _arg2, { cancelToken = CancelToken.none } = {}) => cancelToken; 6);
Discouraged but sometimes necessary way to create a promise.
1import { defer } from "promise-toolbox"; 2 3const { promise, resolve } = defer(); 4 5promise.then((value) => { 6 console.log(value); 7}); 8 9resolve(3);
Easiest and most efficient way to promisify a function call.
1import { fromCallback } from "promise-toolbox"; 2 3// callback is appended to the list of arguments passed to the function 4fromCallback(fs.readFile, "foo.txt").then((content) => { 5 console.log(content); 6}); 7 8// if the callback does not go at the end, you can wrap the call 9fromCallback((cb) => foo("bar", cb, "baz")).then(() => { 10 // ... 11}); 12 13// you can use `.call` to specify the context of execution 14fromCallback.call(thisArg, fn, ...args).then(() => { 15 // ... 16}); 17 18// finally, if you want to call a method, you can pass its name instead of a 19// function 20fromCallback.call(object, "method", ...args).then(() => { 21 // ... 22});
Wait for one event. The first parameter of the emitted event is used to resolve/reject the promise.
1const promise = fromEvent(emitter, "foo", {
2 // whether the promise resolves to an array of all the event args
3 // instead of simply the first arg
4 array: false,
5
6 // whether the error event can reject the promise
7 ignoreErrors: false,
8
9 // name of the error event
10 error: "error",
11});
12
13promise.then(
14 (value) => {
15 console.log("foo event was emitted with value", value);
16 },
17 (reason) => {
18 console.error("an error has been emitted", reason);
19 }
20);
Wait for one of multiple events. The array of all the parameters of the emitted event is used to resolve/reject the promise.
The array also has an
event
property indicating which event has been emitted.
1fromEvents(emitter, ["foo", "bar"], ["error1", "error2"]).then( 2 (event) => { 3 console.log( 4 "event %s have been emitted with values", 5 event.name, 6 event.args 7 ); 8 }, 9 (reasons) => { 10 console.error( 11 "error event %s has been emitted with errors", 12 event.names, 13 event.args 14 ); 15 } 16);
1import { isPromise } from "promise-toolbox"; 2 3if (isPromise(foo())) { 4 console.log("foo() returns a promise"); 5}
From async functions return promises, create new ones taking node-style callbacks.
1import { nodeify } = require('promise-toolbox') 2 3const writable = new Writable({ 4 write: nodeify(async function (chunk, encoding) { 5 // ... 6 }) 7})
Create a new function from the composition of async functions.
1import { pipe } from "promise-toolbox"; 2 3const getUserPreferences = pipe(getUser, getPreferences);
Makes value flow through a list of async functions.
1import { pipe } from "promise-toolbox"; 2 3const output = await pipe( 4 input, // plain value or promise 5 transform1, // sync or async function 6 transform2, 7 transform3 8);
From async functions taking node-style callbacks, create new ones returning promises.
1import fs from "fs"; 2import { promisify, promisifyAll } from "promise-toolbox"; 3 4// Promisify a single function. 5// 6// If possible, the function name is kept and the new length is set. 7const readFile = promisify(fs.readFile); 8 9// Or all functions (own or inherited) exposed on a object. 10const fsPromise = promisifyAll(fs); 11 12readFile(__filename).then((content) => console.log(content)); 13 14fsPromise.readFile(__filename).then((content) => console.log(content));
Retries an async function when it fails.
1import { retry } from "promise-toolbox"; 2 3(async () => { 4 await retry( 5 async () => { 6 const response = await fetch("https://pokeapi.co/api/v2/pokemon/3/"); 7 8 if (response.status === 500) { 9 // no need to retry in this case 10 throw retry.bail(new Error(response.statusText)); 11 } 12 13 if (response.status !== 200) { 14 throw new Error(response.statusText); 15 } 16 17 return response.json(); 18 }, 19 { 20 // predicate when to retry, default on always but programmer errors 21 // (ReferenceError, SyntaxError and TypeError) 22 // 23 // similar to `promise-toolbox/catch`, it can be a constructor, an object, 24 // a function, or an array of the previous 25 when: { message: "my error message" }, 26 27 // this function is called before a retry is scheduled (before the delay) 28 async onRetry(error) { 29 console.warn("attempt", this.attemptNumber, "failed with error", error); 30 console.warn("next try in", this.delay, "milliseconds"); 31 32 // Other information available: 33 // - this.fn: function that failed 34 // - this.arguments: arguments passed to fn 35 // - this.this: context passed to fn 36 37 // This function can throw to prevent any retry. 38 39 // The retry delay will start only after this function has finished. 40 }, 41 42 // delay before a retry, default to 1000 ms 43 delay: 2000, 44 45 // number of tries including the first one, default to 10 46 // 47 // cannot be used with `retries` 48 tries: 3, 49 50 // number of retries (excluding the initial run), default to undefined 51 // 52 // cannot be used with `tries` 53 retries: 4, 54 55 // instead of passing `delay`, `tries` and `retries`, you can pass an 56 // iterable of delays to use to retry 57 // 58 // in this example, it will retry 3 times, first after 1 second, then 59 // after 2 seconds and one last time after 4 seconds 60 // 61 // for more advanced uses, see https://github.com/JsCommunity/iterable-backoff 62 delays: [1e3, 2e3, 4e3], 63 } 64 ); 65})().catch(console.error.bind(console));
The most efficient way to make a function automatically retry is to wrap it:
1MyClass.prototype.myMethod = retry.wrap(MyClass.prototype.myMethod, { 2 delay: 1e3, 3 retries: 10, 4 when: MyError, 5});
In that case options
can also be a function which will be used to compute the options from the context and the arguments:
1MyClass.prototype.myMethod = retry.wrap(
2 MyClass.prototype.myMethod,
3 function getOptions(arg1, arg2) {
4 return this._computeRetryOptions(arg1, arg2);
5 }
6);
Starts a chain of promises.
1import PromiseToolbox from "promise-toolbox"; 2 3const getUserById = (id) => 4 PromiseToolbox.try(() => { 5 if (typeof id !== "number") { 6 throw new Error("id must be a number"); 7 } 8 return db.getUserById(id); 9 });
Note: similar to
Promise.resolve().then(fn)
but callsfn()
synchronously.
Wrap a call to a function to always return a promise.
1function getUserById(id) { 2 if (typeof id !== "number") { 3 throw new TypeError("id must be a number"); 4 } 5 return db.getUser(id); 6} 7 8wrapCall(getUserById, "foo").catch((error) => { 9 // id must be a number 10});
This function can be used as if they were methods, i.e. by passing the promise (or promises) as the context.
This is extremely easy using ES2016's bind syntax.
1const promises = [Promise.resolve("foo"), Promise.resolve("bar")]; 2 3promises::all().then((values) => { 4 console.log(values); 5}); 6// → [ 'foo', 'bar' ]
If you are still an older version of ECMAScript, fear not: simply pass
the promise (or promises) as the first argument of the .call()
method:
1const promises = [Promise.resolve("foo"), Promise.resolve("bar")]; 2 3all.call(promises).then(function (values) { 4 console.log(values); 5}); 6// → [ 'foo', 'bar' ]
Register a node-style callback on this promise.
1import { asCallback } from "promise-toolbox";
2
3// This function can be used either with node-style callbacks or with
4// promises.
5function getDataFor(input, callback) {
6 return dataFromDataBase(input)::asCallback(callback);
7}
Similar to
Promise#catch()
but:
- support predicates
- do not catch
ReferenceError
,SyntaxError
orTypeError
unless they match a predicate because they are usually programmer errors and should be handled separately.
1somePromise 2 .then(() => { 3 return a.b.c.d(); 4 }) 5 ::pCatch(TypeError, ReferenceError, (reason) => { 6 // Will end up here on programmer error 7 }) 8 ::pCatch(NetworkError, TimeoutError, (reason) => { 9 // Will end up here on expected everyday network errors 10 }) 11 ::pCatch((reason) => { 12 // Catch any unexpected errors 13 });
Delays the resolution of a promise by
ms
milliseconds.Note: the rejection is not delayed.
1console.log(await Promise.resolve("500ms passed")::delay(500)); 2// → 500 ms passed
Also works with a value:
1console.log(await delay(500, "500ms passed")); 2// → 500 ms passed
Like setTimeout
in Node, it is possible to
unref
the timer:
1await delay(500).unref();
Iterates in order over a collection, or promise of collection, which contains a mix of promises and values, waiting for each call of cb to be resolved before the next one.
The returned promise will resolve to undefined
when the iteration is
complete.
1["foo", Promise.resolve("bar")]::forEach((value) => { 2 console.log(value); 3 4 // Wait for the promise to be resolve before the next item. 5 return new Promise((resolve) => setTimeout(resolve, 10)); 6}); 7// → 8// foo 9// bar
Ignore (operational) errors for this promise.
1import { ignoreErrors } from "promise-toolbox"; 2 3// will not emit an unhandled rejection error if the file does not 4// exist 5readFileAsync("foo.txt") 6 .then((content) => { 7 console.log(content); 8 }) 9 ::ignoreErrors(); 10 11// will emit an unhandled rejection error due to the typo 12readFileAsync("foo.txt") 13 .then((content) => { 14 console.lgo(content); // typo 15 }) 16 ::ignoreErrors();
Execute a handler regardless of the promise fate. Similar to the
finally
block in synchronous codes.The resolution value or rejection reason of the initial promise is forwarded unless the callback rejects.
1import { pFinally } from "promise-toolbox"; 2 3function ajaxGetAsync(url) { 4 return new Promise((resolve, reject) => { 5 const xhr = new XMLHttpRequest(); 6 xhr.addEventListener("error", reject); 7 xhr.addEventListener("load", resolve); 8 xhr.open("GET", url); 9 xhr.send(null); 10 })::pFinally(() => { 11 $("#ajax-loader-animation").hide(); 12 }); 13}
Returns a promise which resolves to an objects which reflects the resolution of this promise.
1import { reflect } from "promise-toolbox"; 2 3const inspection = await promise::reflect(); 4 5if (inspection.isFulfilled()) { 6 console.log(inspection.value()); 7} else { 8 console.error(inspection.reason()); 9}
Waits for
count
promises in a collection to be resolved.
1import { some } from "promise-toolbox"; 2 3const [first, seconds] = await [ 4 ping("ns1.example.org"), 5 ping("ns2.example.org"), 6 ping("ns3.example.org"), 7 ping("ns4.example.org"), 8]::some(2);
Suppress unhandled rejections, needed when error handlers are attached asynchronously after the promise has rejected.
Similar to
Bluebird#suppressUnhandledRejections()
.
1const promise = getUser()::suppressUnhandledRejections(); 2$(document).on("ready", () => { 3 promise.catch((error) => { 4 console.error("error while getting user", error); 5 }); 6});
Like
.then()
but the original resolution/rejection is forwarded.Like
::finally()
, if the callback rejects, it takes over the original resolution/rejection.
1import { tap } from "promise-toolbox"; 2 3// Contrary to .then(), using ::tap() does not change the resolution 4// value. 5const promise1 = Promise.resolve(42)::tap((value) => { 6 console.log(value); 7}); 8 9// Like .then, the second param is used in case of rejection. 10const promise2 = Promise.reject(42)::tap(null, (reason) => { 11 console.error(reason); 12});
Alias to
promise:tap(null, onRejected)
.
Call a callback if the promise is still pending after
ms
milliseconds. Its resolution/rejection is forwarded.If the callback is omitted, the returned promise is rejected with a
TimeoutError
.
1import { timeout, TimeoutError } from "promise-toolbox"; 2 3await doLongOperation()::timeout(100, () => { 4 return doFallbackOperation(); 5}); 6 7await doLongOperation()::timeout(100); 8 9await doLongOperation()::timeout( 10 100, 11 new Error("the long operation has failed") 12);
Note:
0
is a special value which disable the timeout, useful if the delay is configurable in your app.
# Install dependencies
> npm install
# Run the tests
> npm test
# Continuously compile
> npm run dev
# Continuously run the tests
> npm run dev-test
# Build for production
> npm run build
Contributions are very welcomed, either on the documentation or on the code.
You may:
ISC © Julien Fontanet
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
Found 0/30 approved changesets -- score normalized to 0
Reason
no SAST tool detected
Details
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
license file not detected
Details
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
22 existing vulnerabilities detected
Details
Score
Last Scanned on 2024-11-25
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More