Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/berstend/multi-promise
Run multiple promises in parallel with a strategy
https://github.com/berstend/multi-promise
promise promise-all promise-library promise-pool
Last synced: 3 months ago
JSON representation
Run multiple promises in parallel with a strategy
- Host: GitHub
- URL: https://github.com/berstend/multi-promise
- Owner: berstend
- Created: 2018-08-24T07:11:15.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2019-12-11T20:46:42.000Z (about 5 years ago)
- Last Synced: 2024-11-03T08:51:55.296Z (3 months ago)
- Topics: promise, promise-all, promise-library, promise-pool
- Language: TypeScript
- Size: 43 KB
- Stars: 9
- Watchers: 3
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: readme.md
Awesome Lists containing this project
README
# multi-promise [![ ](https://img.shields.io/bundlephobia/min/multi-promise.svg)](https://bundlephobia.com/[email protected]) [![ ](https://img.shields.io/npm/v/multi-promise.svg)](https://www.npmjs.com/package/multi-promise)
> Run multiple promises in parallel with a strategy
## Motivation
Assume you want to run multiple promises in parallel, how would you go about it?
There's `Promise.all` but this will bail out on the first error, which might not be what you want.
In case you're only interested in one of the results you could consider `Promise.race`, but you can't choose if you want a fulfilled or rejected promise.
To add another requirement to the mix: How about canceling all other promises when one is finished?
`MultiPromise` is a higher level abstraction that makes handling a bunch of promises more fun to work with.
## Features
- Run multiple promises in parallel with a custom strategy
- Built in: `firstSuccess`, `firstError`, `waitForAll`, `bailOnError`
- Optional: `determineSuccessFn` to check if fulfilled promises are really a success
- Optional: `transformResultFn` to return exactly the result you need
- Optional: `cleanupFn` to cancel/abort other promises
- Optional: `timeout` to define a global timeout for all tasks
- Optional: Add an `id` to each task to make later business logic easier
- Generic: Use any kind of promises (no assumptions are being made)
- Written in Typescript but works with JS (Node, Browser) as well
- Lightweight, fast, no dependencies## Installation
`yarn add multi-promise`
or
`npm install --save multi-promise`
## Usage
```typescript
// TypeScript
import { MultiPromise } from 'multi-promise'// ES6 JavaScript
import { MultiPromise } from 'multi-promise'// Legacy JavaScript
const MultiPromise = require('multi-promise').MultiPromise
```## Example
Let's say you want to issue 5 requests in parallel and get the first successful result. You also want some say in what success means, as a non-rejected promise (e.g. statusCode 200) could still mean failure in your business logic. Once a successful request has been made all other running requests should be aborted (no one likes sockets being kept open, right?)
```es6
const { MultiPromise } = require('multi-promise')
const rp = require('request-promise')// define defaults for the request library
const rpx = rp.defaults({
timeout: 20 * 1000,
resolveWithFullResponse: true,
time: true,
simple: false,
json: true
})async function main() {
const mp = new MultiPromise()// add tasks with an optional id
// note the use of a wrapper function to not prematurely run the promises
mp.add(task => rpx({ uri: 'https://invalid.ripe' }), { id: 'ripe' })
mp.add(task => rpx({ uri: 'https://invalid2.ripe' }))
mp.add(task => rpx({ uri: 'https://httpbin.org/delay/3' }), { id: 'foobar1' })
mp.add(task => rpx({ uri: 'https://example.com' }), { id: 'example' })
mp.add(task => rpx({ uri: 'https://httpbin.org/delay/9' }), { id: 'foobar2' })// optional: specify a success callback to conditionally reject promises
// the example.com request will succeed first, but we don't like it :-)
// we can either throw or return false here to reject a fulfilled promise
mp.determineSuccessFn = (task, response) => {
if (response.body.toString().includes('example'))
throw new Error('Example is bad')
}// optional: transform the result property of a fulfilled promise
// the first argument is always the task itself,
// the following arguments are specific to your promises
mp.transformResultFn = (task, response) => ({
statusCode: response.statusCode,
headers: response.headers,
body: response.body
})// optional: clean up once we're finished
// in this case we call `.cancel()` on the request promise
// for all requests that are not finished yet (this will release the sockets)
mp.cleanupFn = tasks => {
tasks.filter(task => !task.isFinished).forEach(task => { task.handle.cancel()) })
}// run all promises in parallel with a strategy
// in this case return the first successful promise
const results = await mp.firstSuccess()
console.log(results)
// => [ { id: 'foobar1', result: { statusCode: 200, headers: [Object], body: [Object] } } ]
}
main()
```## Strategies
##### .firstSuccess()
Return first successful promise, don't mind erroneous ones
##### .firstError()
Return first erroneous promise, ignore successful ones
##### .waitForAll() (`default`)
Return all promises, don't stop on errors
##### .bailOnError()
Stop on erroneous promises (`Promise.all` behaviour)
#### Custom strategy
Example:
```es6
const firstSuccessStrategy = (task, tasks) => {
if (task.isError) task.addToResults = false
if (task.isSuccess) return true
}const mp = new MultiPromise()
const results = await mp.run(firstSuccessStrategy)
```The strategy function is being called whenever a promise returns. By default all promise results are added to the final results array.
Use `task.addToResults = false` to exclude a result. If the strategy function returns `true` then the existing results will be returned and the execution finished.## Options
```typescript
const mp = new MultiProxy({
timeout?: number // optional global timeout
returnUnfinished?: boolean // default: false, will add all promises to results
strategyFn?: MultiPromiseStrategy
determineSuccessFn?: Function
transformResultFn?: Function
cleanupFn?: Function
})
```## Task object
An array of task objects will be returned.
```typescript
interface MultiPromiseTask {
/** User chosen task id of any type */
id?: any
/** Numerical task index, auto-generated */
index?: number
/** Anonymous task function returning a promise */
fn: (dummy?: any) => any
/** Promise handle */
handle?: Promise
isFinished?: boolean
isSuccess?: boolean
isError?: boolean
addToResults?: boolean
result?: any
error?: any
}
```Successful (fulfilled) promises contain a `result` property, erroneous (rejected) promises an `error` property.
In addition an optional `id` and a couple of booleanes are included.
## Debug
```bash
DEBUG=multi-promise node ...
```## License
MIT