https://github.com/robertwhurst/relign
Relign is a little utility library for processing data and tasks in concurrent or serial patterns
https://github.com/robertwhurst/relign
async concurrency javascript parallelism promises tasks typescript
Last synced: 10 months ago
JSON representation
Relign is a little utility library for processing data and tasks in concurrent or serial patterns
- Host: GitHub
- URL: https://github.com/robertwhurst/relign
- Owner: RobertWHurst
- License: mit
- Created: 2016-10-17T03:04:23.000Z (about 9 years ago)
- Default Branch: master
- Last Pushed: 2024-06-23T02:34:57.000Z (over 1 year ago)
- Last Synced: 2024-08-31T05:52:18.404Z (over 1 year ago)
- Topics: async, concurrency, javascript, parallelism, promises, tasks, typescript
- Language: TypeScript
- Homepage:
- Size: 235 KB
- Stars: 39
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: Readme.md
- Funding: .github/FUNDING.yml
- License: license.txt
Awesome Lists containing this project
README
__If you encounter a bug please [report it][bug-report].__
Relign is a little utility library for processing data and tasks in concurrent
or serial patterns. It's heavily inspired by the wonderful library [async][async]
which was widly used back in the bad old days of callback patterns popular in
node programs at the time. Relign is for modern TypeScript and JavaScript programs
that use async functions and promises. It's not just an alternative to Promise.all
but an advanced utility library that fills the gaps in asynchronous patterns,
making them more developer-friendly and maintainable.
Relign is a collection of functions that take tasks or data and process them in
various ways. These functions are all designed to be simple and easy to use
without confusion.
## Installation
Relign is published on [NPM][npm]. It can be used in any TypeScript or JavaScript
project, and is esm or cjs compatible.
```shell
npm i relign
```
```ts
import { parallelMapLimit } from 'relign';
const urlsToFetch: string[] = [
'https://coolsite.com/path/one',
'https://coolsite.com/path/two',
'https://coolsite.com/path/three',
//...
'https://coolsite.com/path/oneThousand',
]
const resultJson = await parallelMapLimit(urlsToFetch, async url => {
const response = await fetch(url);
return response.json();
}, 10);
```
## A Note on Execution
One of the main things that make relign special is how it handles data or tasks.
Relign will happily accept any value as a task. If the value is a function it
will be executed. If the value is a promise it will be awaited. If the value is
a function that returns a promise, the promise will be awaited. If the value is
anything else it will be returned as is. This means that you can pass a mix of
functions, promises, and values to any relign function accepting tasks and it
will do the right thing.
Item functions work in a similar way, but instead these
functions take a worker function that is called for each item in the collection.
This function is executed in the same way as tasks. That means if the worker
returns a promise, the promise will be awaited. If the worker returns a value
that value will be returned as is.
## Function Index
- [parallel](#parallel)
- [parallelLimit](#parallel-limit)
- [series](#series)
- [auto](#auto)
- [parallelMap](#parallel-map)
- [parallelMapLimit](#parallel-map-limit)
- [seriesMap](#series-map)
- [parallelFilter](#parallel-filter)
- [parallelFilterLimit](#parallel-filter-limit)
- [seriesFilter](#series-filter)
- [parallelFind](#parallel-find)
- [parallelFindLimit](#parallel-find-limit)
- [seriesFind](#series-find)
- [parallelFlatMap](#parallel-flat-map)
- [parallelFlatMapLimit](#parallel-flat-map-limit)
- [seriesFlatMap](#series-flat-map)
- [seriesReduce](#series-reduce)
- [nextTick](#next-tick)
- [setTimeout](#set-timeout)
- [setInterval](#set-interval)
- [cbToPromise](#callback-to-promise)
- [exec](#execute)
## Parallel
The `parallel` function takes a collection of tasks and executes them concurrently.
It returns a promise that resolves to an array of the results of each task. It
will happily accept an array or object of tasks. Tasks can be functions, promises,
functions that return a promise, or any other value.
__With an Array__
```ts
import { parallel } from 'relign';
const results = await parallel([
1,
Promise.resolve(2),
async () => 3,
async () => Promise.resolve(4),
])
console.log(results); // [1, 2, 3, 4]
```
__With an Object__
```ts
import { parallel } from 'relign';
const results = await parallel({
one: 1,
two: Promise.resolve(2),
three: async () => 3,
four: async () => Promise.resolve(4),
})
console.log(results); // { one: 1, two: 2, three: 3, four: 4 }
```
## Parallel Limit
The `parallelLimit` function takes a collection of tasks and executes them
concurrently with a limit on the number of tasks that can be executed at once.
It returns a promise that resolves to an array of the results of each task. It
will happily accept an array or object of tasks. Tasks can be functions, promises,
functions that return a promise, or any other value.
__With an Array__
```ts
import { parallelLimit } from 'relign';
const results = await parallelLimit([
1,
Promise.resolve(2),
async () => 3,
async () => Promise.resolve(4),
], 2)
console.log(results); // [1, 2, 3, 4]
```
__With an Object__
```ts
import { parallelLimit } from 'relign';
const results = await parallelLimit({
one: 1,
two: Promise.resolve(2),
three: async () => 3,
four: async () => Promise.resolve(4),
}, 2)
console.log(results); // { one: 1, two: 2, three: 3, four: 4 }
```
## Series
The `series` function takes a collection of tasks and executes them in series.
It returns a promise that resolves to an array of the results of each task. It
will happily accept an array or object of tasks. Tasks can be functions, promises,
functions that return a promise, or any other value.
__With an Array__
```ts
import { series } from 'relign';
const results = await series([
1,
Promise.resolve(2),
async () => 3,
async () => Promise.resolve(4),
])
console.log(results); // [1, 2, 3, 4]
```
__With an Object__
```ts
import { series } from 'relign';
const results = await series({
one: 1,
two: Promise.resolve(2),
three: async () => 3,
four: async () => Promise.resolve(4),
})
console.log(results); // { one: 1, two: 2, three: 3, four: 4 }
```
## Auto
The `auto` function is great when you want tasks to be executed in a specific
order based on dependencies on one another, but still want to run non interdependent
tasks concurrently. It takes an object with a key for each task and a array value
of dependencies and a task. It returns a promise that resolves to an object with
the results of each task. Tasks can be functions, promises, functions that return
promises, or any other value.
```ts
import { auto } from 'relign';
const results = await auto({
one: [1],
two: [Promise.resolve(2)],
three: ['one', 'two', async ({ one, two }) => one + two],
four: ['two', async ({ two }) => two * 2],
})
console.log(results); // { one: 1, two: 2, three: 3, four: 4 }
```
## Parallel Map
The `parallelMap` function takes a collection of items and a map function. It
will then concurrently process each item in the collection with the map function.
Note that the map function can return a promise or any other value. If a promise
is returned, the result of the promise will be used as the value for the item in
the results array. If any other value is returned, that value will be used as is.
__With an Array__
```ts
import { parallelMap } from 'relign';
const results = await parallelMap([1, 2, 3, 4], async (item) => {
return item * 2;
})
console.log(results); // [2, 4, 6, 8]
```
__With an Object__
```ts
import { parallelMap } from 'relign';
const results = await parallelMap({
one: 1,
two: 2,
three: 3,
four: 4,
}, async (item) => {
return item * 2;
})
console.log(results); // { one: 2, two: 4, three: 6, four: 8 }
```
## Parallel Map Limit
The `parallelMapLimit` function takes a collection of items, a map function, and
a limit. It will then concurrently process each item in the collection with the
map function with a limit on the number of items that can be processed at once.
Note that the map function can return a promise or any other value. If a promise
is returned, the result of the promise will be used as the value for the item in
the results array. If any other value is returned, that value will be used as is.
__With an Array__
```ts
import { parallelMapLimit } from 'relign';
const results = await parallelMapLimit([1, 2, 3, 4], async (item) => {
return item * 2;
}, 2)
console.log(results); // [2, 4, 6, 8]
```
__With an Object__
```ts
import { parallelMapLimit } from 'relign';
const results = await parallelMapLimit({
one: 1,
two: 2,
three: 3,
four: 4,
}, async (item) => {
return item * 2;
}, 2)
console.log(results); // { one: 2, two: 4, three: 6, four: 8 }
```
## Series Map
The `seriesMap` function takes a collection of items and a map function. It
will then process each item in the collection with the map function in series.
Note that the map function can return a promise or any other value. If a promise
is returned, the result of the promise will be used as the value for the item in
the results array. If any other value is returned, that value will be used as is.
__With an Array__
```ts
import { seriesMap } from 'relign';
const results = await seriesMap([1, 2, 3, 4], async (item) => {
return item * 2;
})
console.log(results); // [2, 4, 6, 8]
```
__With an Object__
```ts
import { seriesMap } from 'relign';
const results = await seriesMap({
one: 1,
two: 2,
three: 3,
four: 4,
}, async (item) => {
return item * 2;
})
console.log(results); // { one: 2, two: 4, three: 6, four: 8 }
```
## Parallel Filter
The `parallelFilter` function takes a collection of items and a filter function.
It will then concurrently process each item in the collection with the filter
function. Note that the filter function can return a promise that resolves to a
boolean or a boolean. The boolean will be used to determine if the item should
be included in the results.
__With an Array__
```ts
import { parallelFilter } from 'relign';
const results = await parallelFilter([1, 2, 3, 4], async (item) => {
return item % 2 === 0;
})
console.log(results); // [2, 4]
```
__With an Object__
```ts
import { parallelFilter } from 'relign';
const results = await parallelFilter({
one: 1,
two: 2,
three: 3,
four: 4,
}, async (item) => {
return item % 2 === 0;
})
console.log(results); // { two: 2, four: 4 }
```
## Parallel Filter Limit
The `parallelFilterLimit` function takes a collection of items, a filter
function, and a limit. It will then concurrently process each item in the
collection with the filter function with a limit on the number of items that can
be processed at once. Note that the filter function can return a promise that
resolves to a boolean or a boolean. The boolean will be used to determine if the
item should be included in the results.
__With an Array__
```ts
import { parallelFilterLimit } from 'relign';
const results = await parallelFilterLimit([1, 2, 3, 4], async (item) => {
return item % 2 === 0;
}, 2)
console.log(results); // [2, 4]
```
__With an Object__
```ts
import { parallelFilterLimit } from 'relign';
const results = await parallelFilterLimit({
one: 1,
two: 2,
three: 3,
four: 4,
}, async (item) => {
return item % 2 === 0;
}, 2)
console.log(results); // { two: 2, four: 4 }
```
## Series Filter
The `seriesFilter` function takes a collection of items and a filter function.
It will then process each item in the collection with the filter function in
series. Note that the filter function can return a promise that resolves to a
boolean or a boolean. The boolean will be used to determine if the item should
be included in the results.
__With an Array__
```ts
import { seriesFilter } from 'relign';
const results = await seriesFilter([1, 2, 3, 4], async (item) => {
return item % 2 === 0;
})
console.log(results); // [2, 4]
```
__With an Object__
```ts
import { seriesFilter } from 'relign';
const results = await seriesFilter({
one: 1,
two: 2,
three: 3,
four: 4,
}, async (item) => {
return item % 2 === 0;
})
console.log(results); // { two: 2, four: 4 }
```
## Parallel Find
The `parallelFind` function takes a collection of items and a find function. It
will then concurrently execute the find function on each item in the collection
until the find function returns a promise that resolves to true, or true. The
first item to result in true being returned or resolved will be returned as the
result. If no item results in true, undefined will be returned.
__With an Array__
```ts
import { parallelFind } from 'relign';
const result = await parallelFind([1, 2, 3, 4], async (item) => {
return item === 3;
})
console.log(result); // 3
```
__With an Object__
```ts
import { parallelFind } from 'relign';
const result = await parallelFind({
one: 1,
two: 2,
three: 3,
four: 4,
}, async (item) => {
return item === 3;
})
console.log(result); // 3
```
## Parallel Find Limit
The `parallelFindLimit` function takes a collection of items, a find function,
and a limit. It will then concurrently execute the find function on each item in
the collection with a limit on the number of items that can be processed at
once. The find function will be executed on each item in the collection until
the find function returns a promise that resolves to true, or true. The first
item to result in true being returned or resolved will be returned as the
result. If no item results in true, undefined will be returned.
__With an Array__
```ts
import { parallelFindLimit } from 'relign';
const result = await parallelFindLimit([1, 2, 3, 4], async (item) => {
return item === 3;
}, 2)
console.log(result); // 3
```
__With an Object__
```ts
import { parallelFindLimit } from 'relign';
const result = await parallelFindLimit({
one: 1,
two: 2,
three: 3,
four: 4,
}, async (item) => {
return item === 3;
}, 2)
console.log(result); // 3
```
## Series Find
The `seriesFind` function takes a collection of items and a find function. It
will then execute the find function on each item in the collection in series
until the find function returns a promise that resolves to true, or true. The
first item to result in true being returned or resolved will be returned as the
result. If no item results in true, undefined will be returned.
__With an Array__
```ts
import { seriesFind } from 'relign';
const result = await seriesFind([1, 2, 3, 4], async (item) => {
return item === 3;
})
console.log(result); // 3
```
__With an Object__
```ts
import { seriesFind } from 'relign';
const result = await seriesFind({
one: 1,
two: 2,
three: 3,
four: 4,
}, async (item) => {
return item === 3;
})
console.log(result); // 3
```
## Parallel Flat Map
The `parallelFlatMap` function takes a collection of items and a flat map function.
It will then concurrently execute the flat map function on each item in the
collection. Note that the flat map function can return a promise that resolves
to an array or an array. The array will be flattened into the result.
__With an Array__
```ts
import { parallelFlatMap } from 'relign';
const results = await parallelFlatMap([1, 2, 3, 4], async (item) => {
return [item, item];
})
console.log(results); // [1, 1, 2, 2, 3, 3, 4, 4]
```
__With an Object__
```ts
import { parallelFlatMap } from 'relign';
const results = await parallelFlatMap({
one: 1,
two: 2,
three: 3,
four: 4,
}, async (item) => {
return [item, item];
})
console.log(results); // [1, 1, 2, 2, 3, 3, 4, 4]
```
## Parallel Flat Map Limit
The `parallelFlatMapLimit` function takes a collection of items, a flat map
function, and a limit. It will then concurrently execute the flat map function
on each item in the collection with a limit on the number of items that can be
processed at once. Note that the flat map function can return a promise that
resolves to an array or an array. The array will be flattened into the result.
__With an Array__
```ts
import { parallelFlatMapLimit } from 'relign';
const results = await parallelFlatMapLimit([1, 2, 3, 4], async (item) => {
return [item, item];
}, 2)
console.log(results); // [1, 1, 2, 2, 3, 3, 4, 4]
```
__With an Object__
```ts
import { parallelFlatMapLimit } from 'relign';
const results = await parallelFlatMapLimit({
one: 1,
two: 2,
three: 3,
four: 4,
}, async (item) => {
return [item, item];
}, 2)
console.log(results); // [1, 1, 2, 2, 3, 3, 4, 4]
```
## Series Flat Map
The `seriesFlatMap` function takes a collection of items and a flat map
function. It will then execute the flat map function on each item in the
collection in series. Note that the flat map function can return a promise that
resolves to an array or an array. The array will be flattened into the result.
__With an Array__
```ts
import { seriesFlatMap } from 'relign';
const results = await seriesFlatMap([1, 2, 3, 4], async (item) => {
return [item, item];
})
console.log(results); // [1, 1, 2, 2, 3, 3, 4, 4]
```
__With an Object__
```ts
import { seriesFlatMap } from 'relign';
const results = await seriesFlatMap({
one: 1,
two: 2,
three: 3,
four: 4,
}, async (item) => {
return [item, item];
})
console.log(results); // [1, 1, 2, 2, 3, 3, 4, 4]
```
## Series Reduce
The `seriesReduce` function takes a collection of items, a reducer function,
and an initial value. It will then execute the reducer function on each item in
the collection in series. The result of the reducer function can be a promise
or any value. If the reducer function returns a promise, the result of the
promise will be used as the memo for the next iteration, otherwise
the return value will be used as the memo. After all items have been processed,
the memo will be returned as the result.
__With an Array__
```ts
import { seriesReduce } from 'relign';
const result = await seriesReduce([1, 2, 3, 4], async (memo, item) => {
return memo + item;
}, 0)
console.log(result); // 10
```
__With an Object__
```ts
import { seriesReduce } from 'relign';
const result = await seriesReduce({
one: 1,
two: 2,
three: 3,
four: 4,
}, async (memo, item) => {
return memo + item;
}, 0)
console.log(result); // 10
```
## Next Tick
The `nextTick` function returns a promise that resolves on the next tick of the
event loop. If used in a browser, requestAnimationFrame under the hood instead
of node's process.nextTick.
```ts
import { nextTick } from 'relign';
await nextTick();
```
Next tick can also take a task or value as an argument. If a task is passed,
the task will be executed on the next tick of the event loop and the promise
will resolve to the result of the task. If a value is passed, the promise will
resolve to the value on the next tick of the event loop.
```ts
import { nextTick } from 'relign';
const result = await nextTick(() => {
return 'hello world';
});
console.log(result); // 'hello world'
```
## Set Timeout
The `setTimeout` takes a delay and returns a promise that resolves after the
delay has elapsed.
```ts
import { setTimeout } from 'relign';
await setTimeout(1000);
```
Set timeout can also take a task or value as an argument. If a task is passed,
the task will be executed after the delay has elapsed and the promise will
resolve to the result of the task. If a value is passed, the promise will
resolve to the value after the delay has elapsed.
```ts
import { setTimeout } from 'relign';
const result = await setTimeout(1000, () => {
return 'hello world';
});
console.log(result); // 'hello world'
```
## Set Interval
The `setInterval` takes a task and a interval duration. It will call the task
every interval period until the task throws, or executes the resolve function
passed to it. The promise returned by setInterval will not resolve until the
task throws or executes the resolve function passed to it. If the task calls
the resolve function and passes a value, the promise will resolve to that
value.
```ts
import { setInterval } from 'relign';
let count = 0;
const result = await setInterval(1000, (resolve) => {
count += 1;
if (count === 5) {
resolve(count);
}
});
console.log(result); // 5
```
## Callback to Promise
The `cbToPromise` function takes a function that uses the error first callback
pattern and returns a function that returns a promise. The returned function
will call the original function and resolve or reject the promise based on the
error first callback.
```ts
import { cbToPromise } from 'relign';
const readFile = cbToPromise(fs.readFile);
const result = await readFile('./package.json', 'utf8');
console.log(result); // package.json contents
```
## Execute
The `exec` function is the heart of relign and is used by a majority of the
functions above. The execute function takes a task or value and executes it.
If the task is a function, it will be called and the result will be returned.
If the task is a promise, the promise will be awaited and the result will be
returned. If the task is any other value, the value will be returned.
```ts
import { exec } from 'relign';
const result = exec(() => {
return 'hello world';
});
console.log(result); // 'hello world'
```
## Help Welcome
If you want to support this project by throwing be some coffee money It's
greatly appreciated.
[](https://github.com/sponsors/RobertWHurst)
If your interested in providing feedback or would like to contribute please feel
free to do so. I recommend first [opening an issue][feature-request] expressing
your feedback or intent to contribute a change, from there we can consider your
feedback or guide your contribution efforts. Any and all help is greatly
appreciated since this is an open source effort after all.
Thank you!
[npm]: https://www.npmjs.com
[async]: https://github.com/caolan/async
[bug-report]: https://github.com/RobertWHurst/Relign/issues/new?template=bug_report.md
[feature-request]: https://github.com/RobertWHurst/Relign/issues/new?template=feature_request.md