nc.async
Promises and concurrency: map with a limit, queues with priorities, mutexes, polling, and friendlier Promise.allSettled.
const { async } = require("@ix-xs/node-comfort");
import { sleep } from "@ix-xs/node-comfort/async";const pages = await async.map(urls, (url) => fetchPage(url), { concurrency: 5 });
const limit = async.limit(2);
await Promise.all(files.map((file) => limit(() => upload(file))));
await async.poll(() => isServerReady(), { interval: 250, timeout: "10s" });nc.async.Functions
nc.async.sleep()
sleep(duration: string | number, options?: { signal?: AbortSignal; } | undefined): Promise<void>Waits for a while. Same as nc.wait().
Parameters
| Name | Type |
|---|---|
duration | number | string |
optionsoptional | { signal?: AbortSignal } |
Returns Promise<void>
Example
await async.sleep("250ms");nc.async.map()
map<T, R>(items: Iterable<T> | AsyncIterable<T>, fn: (item: T, index: number) => R | PromiseLike<R>, options?: MapOptions): Promise<Awaited<R>[]>Maps items through an async function, with at most concurrency calls at once. Results stay in order.
Parameters
| Name | Type |
|---|---|
items | Iterable<T> | AsyncIterable<T> |
fn | (item: T, index: number) => R | PromiseLike<R> |
optionsoptional | MapOptions |
Returns Promise<Array<Awaited<R>>>
Throws The first error, an AggregateError with stopOnError: false, or an AbortError.
Example
const users = await async.map(ids, (id) => api.getUser(id), { concurrency: 10 });nc.async.forEach()
forEach<T>(items: Iterable<T> | AsyncIterable<T>, fn: (item: T, index: number) => unknown, options?: MapOptions): Promise<void>Runs an async function for every item, with a concurrency limit.
Parameters
| Name | Type |
|---|---|
items | Iterable<T> | AsyncIterable<T> |
fn | (item: T, index: number) => unknown |
optionsoptional | MapOptions |
Returns Promise<void>
Example
await async.forEach(images, (image) => optimize(image), { concurrency: 4 });nc.async.filter()
filter<T>(items: Iterable<T> | AsyncIterable<T>, predicate: (item: T, index: number) => unknown, options?: MapOptions): Promise<T[]>Keeps the items that pass an async test, with a concurrency limit. Order is preserved.
Parameters
| Name | Type |
|---|---|
items | Iterable<T> | AsyncIterable<T> |
predicate | (item: T, index: number) => unknown |
optionsoptional | MapOptions |
Returns Promise<T[]>
Example
const alive = await async.filter(hosts, (host) => ping(host), { concurrency: 20 });nc.async.series()
series<T>(tasks: Iterable<() => T | PromiseLike<T>>): Promise<Awaited<T>[]>Runs async functions one after the other and returns their results.
Parameters
| Name | Type |
|---|---|
tasks | Iterable<() => T | PromiseLike<T>> |
Returns Promise<Array<Awaited<T>>>
Example
await async.series([() => migrateUsers(), () => migrateOrders()]);nc.async.limit()
limit(concurrency: number): LimiterA limiter: wrap calls with it, and at most concurrency run at once while the rest wait their turn. Like p-limit.
Parameters
| Name | Type |
|---|---|
concurrency | number |
Returns Limiter
Example
const limit = async.limit(3);
const results = await Promise.all(urls.map((url) => limit(() => fetch(url))));nc.async.queue()
queue(options?: QueueOptions): QueueA task queue with a concurrency limit, priorities, pause and per-task timeouts.
Parameters
| Name | Type |
|---|---|
optionsoptional | QueueOptions |
Returns Queue
Example
const jobs = async.queue({ concurrency: 2, timeout: "30s" });
jobs.add(() => sendEmail(a));
jobs.add(() => sendEmail(vip), { priority: 10 }); // jumps the line
await jobs.onIdle();nc.async.mutex()
mutex(): MutexA mutex: tasks given to run() never overlap. Put it around read-modify-write steps, like updating a file or refreshing a token.
Returns Mutex
Example
const lock = async.mutex();
await lock.run(async () => {
const data = nc.readJSON("./counter.json", { n: 0 });
nc.writeJSON("./counter.json", { n: data.n + 1 });
});nc.async.deferred()
deferred<T = void>(): Deferred<T>A promise along with its resolve and reject. Handy to connect callbacks or events to await.
Returns Deferred<T>
Example
const ready = async.deferred();
socket.once("open", () => ready.resolve());
await ready.promise;nc.async.settle()
settle<T>(promises: Iterable<T | PromiseLike<T>>): Promise<Settled<Awaited<T>>>Waits for every promise and sorts successes from failures.
Parameters
| Name | Type |
|---|---|
promises | Iterable<T | PromiseLike<T>> |
Returns Promise<Settled<Awaited<T>>>
Example
const { fulfilled, rejected } = await async.settle(emails.map(send));
nc.info(`${fulfilled.length} sent, ${rejected.length} failed`);nc.async.props()
props<T extends Record<string, unknown>>(object: T): Promise<{ [K in keyof T]: Awaited<T[K]>; }>Like Promise.all, for an object of promises.
Parameters
| Name | Type |
|---|---|
object | T |
Returns Promise<{ [K in keyof T]: Awaited<T[K]> }>
Example
const { user, orders } = await async.props({ user: getUser(id), orders: getOrders(id) });nc.async.poll()
poll<T>(fn: () => T | PromiseLike<T>, options?: PollOptions): Promise<NonNullable<Awaited<T>>>Calls fn until it returns something truthy, and resolves with it. Errors count as "not yet".
Parameters
| Name | Type |
|---|---|
fn | () => T | PromiseLike<T> |
optionsoptional | PollOptions |
Returns Promise<NonNullable<Awaited<T>>>
Throws TimeoutError; AbortError
Example
await async.poll(() => fetch(healthUrl).then((res) => res.ok), { interval: "500ms", timeout: "30s" });nc.async.retry()
retry<T>(fn: (attempt: number) => T | PromiseLike<T>, options?: RetryOptions): Promise<Awaited<T>>Calls fn until it succeeds, waiting between attempts. If every attempt fails, the last error is thrown.
Parameters
| Name | Type | Description |
|---|---|---|
fn | (attempt: number) => T | PromiseLike<T> | Receives the attempt number, starting at 1. |
optionsoptional | RetryOptions |
Returns Promise<Awaited<T>>
Throws The last error, or an AbortError if the signal fires.
Example
const data = await func.retry(() => fetchJSON(url), {
attempts: 5,
delay: 200,
backoff: 2, // 200, 400, 800, 1600 ms
shouldRetry: (error) => !(error instanceof nc.errors.HttpError && error.status < 500),
});nc.async.timeout()
timeout<T>(promise: PromiseLike<T> | (() => T | PromiseLike<T>), ms: number, message?: string): Promise<Awaited<T>>Rejects with a TimeoutError if the promise takes longer than ms.
Parameters
| Name | Type | Description |
|---|---|---|
promise | PromiseLike<T> | (() => PromiseLike<T> | T) | A promise, or a function that returns one. |
ms | number | |
messageoptional | string |
Returns Promise<Awaited<T>>
Throws TimeoutError
Example
const res = await func.timeout(fetch(url), 5000);
const rows = await func.timeout(() => slowQuery(), 2000, "Database too slow");Types
Import any of them in TypeScript with import type { Deferred } from "@ix-xs/node-comfort", or in JavaScript with import("@ix-xs/node-comfort").Deferred.
Deferred
A promise with its resolve and reject.
| Property | Type | Description |
|---|---|---|
promise | Promise<T> | |
resolve | (value: T | PromiseLike<T>) => void | |
reject | (reason?: unknown) => void | |
settled | boolean | Whether it's already resolved or rejected. |
Limiter
A limiter from limit(). Call it with a function to run it when there's room.
type Limiter = (<T>(fn: () => T | PromiseLike<T>) => Promise<Awaited<T>>) & { readonly activeCount: number; readonly pendingCount: number; concurrency: number; clearQueue(): void; onIdle(): Promise<void>; }MapOptions
Options for map(), forEach() and filter().
| Property | Type | Description |
|---|---|---|
concurrencyoptional | number | How many run at the same time. No limit by default. |
stopOnErroroptional | boolean | Stop at the first error. With false, everything runs and you get an AggregateError of all failures. Defaults to true. |
signaloptional | AbortSignal | Stops starting new work and rejects with an AbortError. |
Mutex
A mutex from mutex().
| Property | Type | Description |
|---|---|---|
run | <T>(fn: () => T | PromiseLike<T>) => Promise<Awaited<T>> | Runs fn once no other run is in progress. |
lock | () => Promise<() => void> | Waits for the lock and gives you the function that releases it. |
isLocked | boolean |
PollOptions
Options for poll().
| Property | Type | Description |
|---|---|---|
intervaloptional | string | number | Time between tries. Defaults to 100 ms. |
timeoutoptional | string | number | Give up with a TimeoutError after this long. |
signaloptional | AbortSignal | Stops with an AbortError. |
Queue
A queue from queue().
| Property | Type | Description |
|---|---|---|
add | <T>(task: () => T | PromiseLike<T>, options?: QueueAddOptions) => Promise<Awaited<T>> | Adds a task; resolves with its result. |
addAll | <T>(tasks: (() => T | PromiseLike<T>)[], options?: QueueAddOptions) => Promise<Awaited<T>[]> | Adds several tasks; resolves with all results. |
pause | () => Queue | Stops starting tasks. Running ones finish. |
start | () => Queue | Starts again. |
clear | () => void | Drops the tasks that haven't started. Their promises never settle. |
onIdle | () => Promise<void> | Resolves when nothing is waiting or running. |
onEmpty | () => Promise<void> | Resolves when nothing is waiting. |
size | number | Tasks waiting. |
pending | number | Tasks running. |
isPaused | boolean | |
concurrency | number | Can be changed on the fly. |
QueueAddOptions
Options for queue.add().
| Property | Type | Description |
|---|---|---|
priorityoptional | number | Higher goes first; equal priorities keep their order. Defaults to 0. |
QueueOptions
Options for queue().
| Property | Type | Description |
|---|---|---|
concurrencyoptional | number | How many tasks run at the same time. Defaults to 1. |
autoStartoptional | boolean | Start right away. Defaults to true. |
timeoutoptional | string | number | Time limit per task, like "30s". A slower task rejects with a TimeoutError. |
Settled
What settle() returns.
| Property | Type | Description |
|---|---|---|
fulfilled | T[] | Successful values, in order. |
rejected | unknown[] | Errors, in order. |
results | PromiseSettledResult<T>[] | Everything, like Promise.allSettled. |