Async
35 exports from @ontologyio/receta/async.
Everything exported from @ontologyio/receta/async. New here? Start with the Async guide.
Functions
batch
Processes items in batches with optional delay between batches.
Returns a Result to handle errors explicitly. If any batch processing fails, the entire operation returns an Err with details about which batch failed.
fn runs once per batch and receives the whole batch, so the result holds
one entry per batch rather than one per item.
Useful for bulk operations like data imports, exports, or processing large datasets without overwhelming the system.
function batch<T, U>(items: readonly T[], fn: (batch: readonly T[]) => Promise<U>, options?: BatchOptions): Promise<Result<U[], BatchError>>Fails with BatchError.
import * as R from 'remeda'
import { unwrapOr, tapErr } from '@ontologyio/receta/result'
// Process users in batches of 50
const result = await batch(
users,
async (userBatch) => {
return db.users.insertMany(userBatch)
},
{
batchSize: 50,
delayBetweenBatches: 1000, // 1 second between batches
}
)
// Handle with Result pattern
const logged = tapErr(result, error => console.error('Batch failed:', error))
const inserted = unwrapOr(logged, [])
// Send emails in batches to avoid rate limits
const emailResult = await batch(
emailAddresses,
async (batch) => sendBulkEmail(batch),
{
batchSize: 100,
delayBetweenBatches: 5000, // 5 seconds between batches
}
)
// Import large dataset with error handling
const imported = await batch(
records,
async (recordBatch) => {
console.log(`Importing batch of ${recordBatch.length}`)
return importRecords(recordBatch)
},
{ batchSize: 1000, concurrency: 2 } // Process 2 batches concurrently
)See also mapAsync, parallel, batchOrThrow.
batchOrThrow
Throwing variant of batch for backward compatibility.
Use this when you want exceptions instead of Result pattern. Prefer the Result-returning batch for better error handling.
function batchOrThrow<T, U>(items: readonly T[], fn: (batch: readonly T[]) => Promise<U>, options?: BatchOptions | undefined): Promise<U[]>// Throws on error
try {
const results = await batchOrThrow(
users,
async (userBatch) => db.users.insertMany(userBatch),
{ batchSize: 50 }
)
} catch (error) {
console.error('Batch failed:', error)
}See also batch, orThrow.
chunk
Split an array into groups the length of size. If array can't be split evenly, the final chunk will be the remaining elements.
function chunk<T extends IterableContainer, N extends number>(array: T, size: N): Chunk<T, N>function chunk<N extends number>(size: N): <T extends IterableContainer>(array: T) => Chunk<T, N>chunk(['a', 'b', 'c', 'd'], 2) // => [['a', 'b'], ['c', 'd']]
chunk(['a', 'b', 'c', 'd'], 3) // => [['a', 'b', 'c'], ['d']]chunk(2)(['a', 'b', 'c', 'd']) // => [['a', 'b'], ['c', 'd']]
chunk(3)(['a', 'b', 'c', 'd']) // => [['a', 'b', 'c'], ['d']]debounce
Debounces an async function.
Creates a debounced version of an async function that delays invoking until after the specified delay has elapsed since the last call.
Useful for rate-limiting API calls, search inputs, or any operation that shouldn't run on every invocation.
Every call settles. A call that a newer one replaces gets the result of the run
that replaced it, and with trailing: false, a call ignored during the delay gets
the result of the last leading run. If leading and trailing are both false,
nothing can run, so every call rejects. There is no cancel or flush.
function debounce<TArgs extends readonly unknown[], TReturn>(fn: (...args: TArgs) => Promise<TReturn>, options: DebounceOptions): (...args: TArgs) => Promise<TReturn>// Debounced search
const searchAPI = debounce(
async (query: string) => {
const res = await fetch(`/api/search?q=${query}`)
return res.json()
},
{ delay: 300 }
)
// User types "hello" quickly
searchAPI('h') // Replaced, settles with the result for 'hello'
searchAPI('he') // Replaced
searchAPI('hel') // Replaced
searchAPI('hell') // Replaced
searchAPI('hello') // Executes after 300ms
// Leading edge debounce (execute immediately, ignore subsequent calls)
const saveData = debounce(
async (data) => api.save(data),
{ delay: 1000, leading: true, trailing: false }
)See also throttle.
extractFulfilled
Extracts all fulfilled values from settled results.
Filters out rejected promises and returns only successful values. Useful when you want to process partial results even if some promises failed.
function extractFulfilled<T>(results: readonly PromiseSettledResult<T>[]): T[]const results = await promiseAllSettled([
Promise.resolve(1),
Promise.reject('error'),
Promise.resolve(3),
])
const values = extractFulfilled(results)
// => [1, 3]// Process partial API results
const results = await promiseAllSettled(
userIds.map((id) => fetchUser(id))
)
const successfulUsers = extractFulfilled(results)
console.log(`Fetched ${successfulUsers.length} of ${userIds.length} users`)extractRejected
Extracts all rejection reasons from settled results.
Filters out fulfilled promises and returns only error reasons. Useful for logging or analyzing failures.
function extractRejected<T = unknown>(results: readonly PromiseSettledResult<any>[]): T[]const results = await promiseAllSettled([
Promise.resolve(1),
Promise.reject(new Error('Network error')),
Promise.reject(new Error('Timeout')),
])
const errors = extractRejected<Error>(results)
// => [Error('Network error'), Error('Timeout')]
errors.forEach((err) => console.error('Failed:', err.message))// Track failed operations
const results = await promiseAllSettled(
userIds.map((id) => deleteUser(id))
)
const failures = extractRejected(results)
if (failures.length > 0) {
console.warn(`${failures.length} deletions failed:`, failures)
}filterAsync
Filters an array using an async predicate function.
Returns a Result to handle errors explicitly. If any predicate evaluation fails, the entire operation returns an Err with details about the failure.
function filterAsync<T>(items: readonly T[], predicate: (item: T, index: number) => Promise<boolean>, options?: ConcurrencyOptions | undefined): Promise<Result<T[], FilterAsyncError>>function filterAsync<T>(predicate: (item: T, index: number) => Promise<boolean>, options?: ConcurrencyOptions | undefined): (items: readonly T[]) => Promise<Result<T[], FilterAsyncError>>Fails with FilterAsyncError.
import * as R from 'remeda'
import { unwrapOr, tapErr } from '@ontologyio/receta/result'
// Data-first: Filter users who exist
const result = await filterAsync(
userIds,
async (id) => {
const res = await fetch(`/api/users/${id}`)
return res.ok
},
{ concurrency: 5 }
)
// Handle with Result pattern
const logged = tapErr(result, error => console.error('Filter failed:', error))
const existingUsers = unwrapOr(logged, [])
// Data-last (pipeAsync awaits each step)
const filtered = await pipeAsync(
urls,
filterAsync(isReachable, { concurrency: 3 }),
(result) => unwrapOr(result, [])
)See also mapAsync, filterAsyncOrThrow.
filterAsyncOrThrow
Throwing variant of filterAsync for backward compatibility.
Use this when you want exceptions instead of Result pattern. Prefer the Result-returning filterAsync for better error handling.
function filterAsyncOrThrow<T>(items: readonly T[], predicate: (item: T, index: number) => Promise<boolean>, options?: ConcurrencyOptions | undefined): Promise<T[]>function filterAsyncOrThrow<T>(predicate: (item: T, index: number) => Promise<boolean>, options?: ConcurrencyOptions | undefined): (items: readonly T[]) => Promise<T[]>// Throws on error
try {
const existingUsers = await filterAsyncOrThrow(
userIds,
async (id) => {
const res = await fetch(`/api/users/${id}`)
return res.ok
},
{ concurrency: 5 }
)
} catch (error) {
console.error('Filter failed:', error)
}See also filterAsync, orThrow.
mapAsync
Maps over an array with an async function, with optional concurrency control.
Returns a Result to handle errors explicitly. If any mapper function fails, the entire operation returns an Err with details about the failure.
This is more powerful than Promise.all(items.map(fn)) because it:
- Allows limiting concurrent operations (rate limiting, resource management)
- Returns Result for explicit error handling
- Provides detailed error information (which item failed, when
concurrencyis below the item count)
function mapAsync<T, U>(items: readonly T[], fn: (item: T, index: number) => Promise<U>, options?: ConcurrencyOptions | undefined): Promise<Result<U[], MapAsyncError>>function mapAsync<T, U>(fn: (item: T, index: number) => Promise<U>, options?: ConcurrencyOptions | undefined): (items: readonly T[]) => Promise<Result<U[], MapAsyncError>>Fails with MapAsyncError.
import * as R from 'remeda'
import { unwrapOr, tapErr } from '@ontologyio/receta/result'
// Data-first: Fetch users with concurrency limit
const result = await mapAsync(
['user1', 'user2', 'user3'],
async (id) => fetch(`/api/users/${id}`).then(r => r.json()),
{ concurrency: 2 }
)
// Handle with Result pattern
const logged = tapErr(result, error => console.error('Failed at index:', error.index))
const users = unwrapOr(logged, [])
// Data-last (pipeAsync awaits each step)
const processed = await pipeAsync(
userIds,
mapAsync(fetchUser, { concurrency: 5 }),
(result) => unwrapOr(result, [])
)
// Unlimited concurrency (same as Promise.all but safer)
const all = await mapAsync(urls, fetchJSON)See also filterAsync, parallel, mapAsyncOrThrow.
mapAsyncOrThrow
Throwing variant of mapAsync for backward compatibility.
Use this when you want exceptions instead of Result pattern. Prefer the Result-returning mapAsync for better error handling.
This is implemented using the orThrow utility from Result module,
which converts any Result-returning function to a throwing variant.
function mapAsyncOrThrow<T, U>(items: readonly T[], fn: (item: T, index: number) => Promise<U>, options?: ConcurrencyOptions | undefined): Promise<U[]>function mapAsyncOrThrow<T, U>(fn: (item: T, index: number) => Promise<U>, options?: ConcurrencyOptions | undefined): (items: readonly T[]) => Promise<U[]>// Throws on error
try {
const users = await mapAsyncOrThrow(
userIds,
async (id) => fetchUser(id),
{ concurrency: 5 }
)
} catch (error) {
console.error('Failed:', error)
}See also mapAsync, orThrow.
parallel
Executes an array of async tasks in parallel with optional concurrency limit.
Similar to Promise.all but with concurrency control, and it returns a Result instead of rejecting. Useful when you have a list of async operations and want to limit how many run simultaneously.
function parallel<T>(tasks: readonly (() => Promise<T>)[], options?: ConcurrencyOptions | undefined): Promise<Result<T[], MapAsyncError>>Fails with MapAsyncError.
// Execute all tasks with concurrency limit of 3
const results = await parallel(
[
() => fetch('/api/users/1'),
() => fetch('/api/users/2'),
() => fetch('/api/users/3'),
() => fetch('/api/users/4'),
() => fetch('/api/users/5'),
],
{ concurrency: 3 }
)
// Unlimited concurrency (same as Promise.all)
const all = await parallel([
() => fetchUser(1),
() => fetchPosts(),
() => fetchComments(),
])
// Generate tasks dynamically
const userIds = [1, 2, 3, 4, 5]
const fetchTasks = userIds.map(id => () => fetchUser(id))
const users = await parallel(fetchTasks, { concurrency: 2 })See also sequential, mapAsync.
parallelOrThrow
Throwing variant of parallel for backward compatibility.
Use this when you want exceptions instead of Result pattern. Prefer the Result-returning parallel for better error handling.
This is implemented using the orThrow utility from Result module,
which converts any Result-returning function to a throwing variant.
function parallelOrThrow<T>(tasks: readonly (() => Promise<T>)[], options?: ConcurrencyOptions | undefined): Promise<T[]>// Equivalent to using orThrow directly
const parallelOrThrow = orThrow(parallel)See also parallel, orThrow.
pipeAsync
Composes async functions from left to right.
Like Remeda's pipe, but handles async functions by awaiting each step
before passing the result to the next function. Useful for chaining
async transformations in a readable, sequential manner.
Note: All functions are executed sequentially (not in parallel).
For parallel execution, use parallel or mapAsync.
function pipeAsync<A, B>(value: A, fn1: (a: A) => B | Promise<B>): Promise<B>function pipeAsync<A, B, C>(value: A, fn1: (a: A) => B | Promise<B>, fn2: (b: B) => C | Promise<C>): Promise<C>function pipeAsync<A, B, C, D>(value: A, fn1: (a: A) => B | Promise<B>, fn2: (b: B) => C | Promise<C>, fn3: (c: C) => D | Promise<D>): Promise<D>function pipeAsync<A, B, C, D, E>(value: A, fn1: (a: A) => B | Promise<B>, fn2: (b: B) => C | Promise<C>, fn3: (c: C) => D | Promise<D>, fn4: (d: D) => E | Promise<E>): Promise<E>function pipeAsync<A, B, C, D, E, F>(value: A, fn1: (a: A) => B | Promise<B>, fn2: (b: B) => C | Promise<C>, fn3: (c: C) => D | Promise<D>, fn4: (d: D) => E | Promise<E>, fn5: (e: E) => F | Promise<F>): Promise<F>function pipeAsync<A, B, C, D, E, F, G>(value: A, fn1: (a: A) => B | Promise<B>, fn2: (b: B) => C | Promise<C>, fn3: (c: C) => D | Promise<D>, fn4: (d: D) => E | Promise<E>, fn5: (e: E) => F | Promise<F>, fn6: (f: F) => G | Promise<G>): Promise<G>function pipeAsync<A, B, C, D, E, F, G, H>(value: A, fn1: (a: A) => B | Promise<B>, fn2: (b: B) => C | Promise<C>, fn3: (c: C) => D | Promise<D>, fn4: (d: D) => E | Promise<E>, fn5: (e: E) => F | Promise<F>, fn6: (f: F) => G | Promise<G>, fn7: (g: G) => H | Promise<H>): Promise<H>function pipeAsync<A, B, C, D, E, F, G, H, I>(value: A, fn1: (a: A) => B | Promise<B>, fn2: (b: B) => C | Promise<C>, fn3: (c: C) => D | Promise<D>, fn4: (d: D) => E | Promise<E>, fn5: (e: E) => F | Promise<F>, fn6: (f: F) => G | Promise<G>, fn7: (g: G) => H | Promise<H>, fn8: (h: H) => I | Promise<I>): Promise<I>function pipeAsync<A, B, C, D, E, F, G, H, I, J>(value: A, fn1: (a: A) => B | Promise<B>, fn2: (b: B) => C | Promise<C>, fn3: (c: C) => D | Promise<D>, fn4: (d: D) => E | Promise<E>, fn5: (e: E) => F | Promise<F>, fn6: (f: F) => G | Promise<G>, fn7: (g: G) => H | Promise<H>, fn8: (h: H) => I | Promise<I>, fn9: (i: I) => J | Promise<J>): Promise<J>function pipeAsync<A, B, C, D, E, F, G, H, I, J, K>(value: A, fn1: (a: A) => B | Promise<B>, fn2: (b: B) => C | Promise<C>, fn3: (c: C) => D | Promise<D>, fn4: (d: D) => E | Promise<E>, fn5: (e: E) => F | Promise<F>, fn6: (f: F) => G | Promise<G>, fn7: (g: G) => H | Promise<H>, fn8: (h: H) => I | Promise<I>, fn9: (i: I) => J | Promise<J>, fn10: (j: J) => K | Promise<K>): Promise<K>// Sequential API calls
const result = await pipeAsync(
userId,
(id) => fetchUser(id),
(user) => fetchUserPosts(user.id),
(posts) => posts.filter(p => p.published),
(posts) => posts.map(p => p.title)
)
// => ['Post 1', 'Post 2', ...]// Mix sync and async functions
const processOrder = await pipeAsync(
orderId,
(id) => fetchOrder(id), // async
(order) => order.items, // sync
async (items) => { // async
const prices = await Promise.all(items.map(getPriceAsync))
return prices
},
(prices) => prices.reduce((a, b) => a + b, 0) // sync
)// Error handling with Result pattern
import { tryCatchAsync, map, unwrapOr } from '@ontologyio/receta/result'
const safeResult = await pipeAsync(
userId,
(id) => tryCatchAsync(() => fetchUser(id)),
(userResult) => map(userResult, u => u.email)
)
const email = unwrapOr(safeResult, 'unknown@example.com')// Data transformation pipeline
const report = await pipeAsync(
'raw-data.json',
(file) => fs.readFile(file, 'utf-8'),
(content) => JSON.parse(content),
async (data) => {
const enriched = await enrichData(data)
return enriched
},
(data) => generateReport(data)
)See also sequential, https://remedajs.com/docs#pipe.
poll
Polls an async function until it returns a value or reaches max attempts.
Useful for waiting on async operations like job completion, order status, or resource availability. Returns Result for explicit error handling.
null, undefined and false mean "not yet". Anything else, including 0
and '', stops polling and becomes the Ok value. If fn throws, the returned
promise rejects instead of resolving to an Err.
function poll<T>(fn: () => Promise<false | T | null | undefined>, options?: PollOptions): Promise<Result<T, PollError>>Fails with PollError.
import * as R from 'remeda'
import { unwrapOr, mapErr } from '@ontologyio/receta/result'
// Poll until job is complete
const result = await poll(
async () => {
const status = await checkJobStatus(jobId)
return status.state === 'completed' ? status : null
},
{
interval: 1000, // Check every second
maxAttempts: 30, // Max 30 attempts (30 seconds)
}
)
// Handle with Result pattern
const job = R.pipe(
result,
mapErr(error => {
if (error.type === 'max_attempts') {
return 'Job check timed out'
}
return 'Job check failed'
}),
unwrapOr(null)
)
// Poll with timeout
const order = await poll(
async () => fetchOrderStatus(orderId),
{
interval: 2000,
maxAttempts: 30,
timeout: 60000, // Fail after 1 minute
onPoll: (attempt) => console.log(`Checking... attempt ${attempt}`)
}
)
// Poll until condition is met
const result = await poll(
async () => {
const data = await fetchData()
return data.ready ? data : null
},
{
interval: 500,
maxAttempts: 50,
shouldContinue: (attempt) => attempt < 20
}
)See also retry.
promiseAllSettled
Typed wrapper around Promise.allSettled with better type inference.
Unlike Promise.all (which fails fast on first rejection), this waits for all promises to settle (fulfill or reject) before returning results. Provides helper functions to extract fulfilled/rejected values.
function promiseAllSettled<T>(promises: readonly Promise<T>[]): Promise<PromiseSettledResult<T>[]>// Basic usage
const results = await promiseAllSettled([
fetch('/api/users/1'),
fetch('/api/users/2'),
fetch('/api/users/999'), // might fail
])
results.forEach((result) => {
if (result.status === 'fulfilled') {
console.log('Success:', result.value)
} else {
console.log('Error:', result.reason)
}
})// Extract only successful results
const results = await promiseAllSettled([
fetchUser(1),
fetchUser(2),
fetchUser(999), // fails
])
const users = extractFulfilled(results)
// => [User1, User2] (failed one excluded)// Convert to Result pattern for composability
import * as R from 'remeda'
import { map, unwrapOr } from '@ontologyio/receta/result'
const results = await promiseAllSettled([
fetchUser(1),
fetchUser(2),
fetchUser(3),
])
const resultArray = toResults(results)
const usernames = R.pipe(
resultArray,
R.map((r) => map(r, (user) => user.name)),
R.map((r) => unwrapOr(r, 'Unknown'))
)
// => ['John', 'Jane', 'Unknown']See also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled.
retry
Retries an async function with exponential backoff, returning a Result.
Useful for handling transient failures like network errors, rate limits, or temporary service unavailability. Returns Result for explicit error handling.
fn runs again when it throws or rejects. A function that returns an Err has
succeeded as far as retry can tell, so throw to ask for another attempt.
function retry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<Result<T, RetryError>>Fails with RetryError.
import * as R from 'remeda'
import { map, tapErr, unwrapOr } from '@ontologyio/receta/result'
// Basic retry with defaults (3 attempts, exponential backoff)
const result = await retry(async () => {
const res = await fetch('/api/data')
if (!res.ok) throw new Error(`HTTP ${res.status}`)
return res.json()
})
// Handle with Result pattern
const data = R.pipe(
result,
tapErr(error => console.error('Failed after retries:', error)),
unwrapOr({ default: 'data' })
)
// Custom retry options
const customResult = await retry(
async () => fetchData(),
{
maxAttempts: 5,
delay: 500,
backoff: 2,
maxDelay: 10000,
jitter: 250, // add 0-250ms of random delay to avoid thundering herds
shouldRetry: (error, attempt) => {
// Only retry on specific errors
return error instanceof NetworkError && attempt < 3
},
onRetry: (error, attempt, delay) => {
console.log(`Retry ${attempt} after ${delay}ms:`, error)
}
}
)
// Compose with other Result operations
const processed = R.pipe(
await retry(() => fetchUser(id)),
map(user => user.email),
unwrapOr('noreply@example.com')
)See also poll, timeout.
sequential
Executes an array of async tasks sequentially (one at a time).
Returns a Result to handle errors explicitly. If any task fails, execution stops and returns an Err with details about which task failed.
Each task waits for the previous one to complete before starting. Useful when tasks have dependencies or when you need strict ordering.
function sequential<T>(tasks: readonly (() => Promise<T>)[]): Promise<Result<T[], SequentialError>>Fails with SequentialError.
import * as R from 'remeda'
import { isErr, tapErr, unwrapOr } from '@ontologyio/receta/result'
// Execute tasks one at a time in order
const result = await sequential([
() => createUser({ name: 'Alice' }),
() => createPost({ title: 'Hello' }),
() => publishPost({ id: 1 }),
])
// Handle with Result pattern
const logged = tapErr(result, error => console.error('Task failed at index:', error.taskIndex))
const results = unwrapOr(logged, [])
// Database migrations that must run in order
const migrationResult = await sequential([
() => runMigration('001_create_users'),
() => runMigration('002_add_email_index'),
() => runMigration('003_create_posts'),
])
if (isErr(migrationResult)) {
console.error('Migration failed:', migrationResult.error)
// Can rollback completed migrations using completedTasks count
}
// Process items with side effects in order
const logs = await sequential(
events.map(event => () => processEvent(event))
)See also parallel, sequentialOrThrow.
sequentialOrThrow
Throwing variant of sequential for backward compatibility.
Use this when you want exceptions instead of Result pattern. Prefer the Result-returning sequential for better error handling.
function sequentialOrThrow<T>(tasks: readonly (() => Promise<T>)[]): Promise<T[]>// Throws on error
try {
const results = await sequentialOrThrow([
() => createUser({ name: 'Alice' }),
() => createPost({ title: 'Hello' }),
() => publishPost({ id: 1 }),
])
} catch (error) {
console.error('Task failed:', error)
}See also sequential, orThrow.
sleep
Sleep for a specified duration.
function sleep(ms: number): Promise<void>// Wait 1 second
await sleep(1000)
// Use in sequence
console.log('Starting...')
await sleep(2000)
console.log('2 seconds later')throttle
Throttles an async function.
Creates a throttled version of an async function that only invokes at most once per specified delay period.
Unlike debounce (which delays execution until calls stop), throttle guarantees execution at regular intervals regardless of call frequency.
Calls made during the delay share one promise for the next trailing run, which
uses the latest arguments. There is no cancel or flush.
function throttle<TArgs extends readonly unknown[], TReturn>(fn: (...args: TArgs) => Promise<TReturn>, options: ThrottleOptions): (...args: TArgs) => Promise<TReturn>// Throttled API call (max once per second)
const trackEvent = throttle(
async (event: string) => {
await fetch('/api/analytics', {
method: 'POST',
body: JSON.stringify({ event })
})
},
{ delay: 1000 }
)
// User scrolls rapidly
trackEvent('scroll') // Executes immediately
trackEvent('scroll') // Waits for the trailing run
trackEvent('scroll') // Shares that run, which uses the latest arguments
// ... 1 second passes, and the trailing run executes
// Throttle with trailing edge only
const saveProgress = throttle(
async (data) => api.save(data),
{ delay: 5000, leading: false, trailing: true }
)See also debounce.
timeout
Adds a timeout to a promise, returning a Result.
If the promise doesn't resolve within the specified time, returns an Err with TimeoutError for explicit error handling.
Only the timeout becomes an Err: if the promise rejects first, that rejection is rethrown. The promise keeps running after a timeout, because timeout stops waiting rather than cancelling the work.
function timeout<T>(promise: Promise<T>, ms: number): Promise<Result<T, TimeoutError>>Fails with TimeoutError.
import * as R from 'remeda'
import { isErr, map, tapErr, unwrapOr } from '@ontologyio/receta/result'
// Basic timeout
const result = await timeout(
fetch('/api/data').then(r => r.json()),
5000 // 5 second timeout
)
// Handle with Result pattern
const data = R.pipe(
result,
tapErr(error => console.error('Timeout:', error)),
unwrapOr({ fallback: 'data' })
)
// Compose with other async operations
const processed = R.pipe(
await timeout(fetchUser(id), 3000),
map(user => user.email),
unwrapOr('noreply@example.com')
)
// Check timeout errors explicitly
const result = await timeout(slowOperation(), 1000)
if (isErr(result) && result.error instanceof TimeoutError) {
console.log('Operation timed out')
}See also retry, poll.
toResults
Converts settled results to Result array for composability.
Transforms native PromiseSettledResult into Receta's Result type, enabling composition with Result utilities (map, flatMap, etc.).
function toResults<T, E = unknown>(results: readonly PromiseSettledResult<T>[]): Result<T, E>[]import * as R from 'remeda'
import { map, unwrapOr } from '@ontologyio/receta/result'
const results = await promiseAllSettled([
fetchUser(1),
fetchUser(999), // fails
])
const userResults = toResults(results)
const names = R.pipe(
userResults,
R.map((r) => map(r, (user) => user.name)),
R.map((r) => unwrapOr(r, 'Unknown'))
)
// => ['John', 'Unknown']import { partition } from '@ontologyio/receta/result'
const results = await promiseAllSettled([
fetchUser(1),
fetchUser(2),
fetchUser(999),
])
const [successes, failures] = partition(toResults(results))
console.log(`${successes.length} succeeded, ${failures.length} failed`)// Compose with validation
import { flatMap } from '@ontologyio/receta/result'
import { validate } from '@ontologyio/receta/validation'
const results = await promiseAllSettled(
urls.map((url) => fetch(url).then(r => r.json()))
)
const validated = R.pipe(
toResults(results),
R.map((r) => flatMap(r, (data) => validateSchema(userSchema, data)))
)Values
TimeoutError
Error type for timeout failures.
const TimeoutError: typeof TimeoutErrorTypes
BatchError
Error type for batch processing failures.
interface BatchError {
readonly type: 'batch_error'
readonly batchIndex: number
readonly itemsInBatch: number
readonly underlyingError: MapAsyncError | unknown
}BatchOptions
Options for batch operations.
interface BatchOptions {
readonly batchSize?: number
readonly delayBetweenBatches?: number
readonly concurrency?: number
}ConcurrencyOptions
Options for concurrent operations.
interface ConcurrencyOptions {
readonly concurrency?: number
}DebounceOptions
Options for debounce operations.
interface DebounceOptions {
readonly delay: number
readonly leading?: boolean
readonly trailing?: boolean
}FilterAsyncError
Error type for filterAsync failures.
interface FilterAsyncError {
readonly type: 'filter_async_error'
readonly underlyingError: MapAsyncError
}MapAsyncError
Error type for mapAsync failures.
interface MapAsyncError {
readonly type: 'map_async_error'
readonly index: number
readonly item: unknown
readonly error: unknown
}PollError
Error type returned when polling fails.
type PollError =
| { readonly type: 'max_attempts'; readonly attempts: number }
| { readonly type: 'timeout'; readonly elapsed: number }
| { readonly type: 'stopped'; readonly attempt: number }PollOptions
Options for polling operations.
interface PollOptions {
readonly interval?: number
readonly maxAttempts?: number
readonly timeout?: number
readonly shouldContinue?: (attempt: number) => boolean
readonly onPoll?: (attempt: number) => void
}RetryError
Error type returned when all retry attempts fail.
interface RetryError {
readonly type: 'max_attempts_exceeded'
readonly lastError: unknown
readonly attempts: number
}RetryOptions
Options for retry operations.
interface RetryOptions {
readonly maxAttempts?: number
readonly delay?: number
readonly backoff?: number
readonly maxDelay?: number
readonly jitter?: number
readonly shouldRetry?: (error: unknown, attempt: number) => boolean
readonly onRetry?: (error: unknown, attempt: number, delay: number) => void
}SequentialError
Error type for sequential task execution failures.
interface SequentialError {
readonly type: 'sequential_error'
readonly taskIndex: number
readonly completedTasks: number
readonly error: unknown
}ThrottleOptions
Options for throttle operations.
interface ThrottleOptions {
readonly delay: number
readonly leading?: boolean
readonly trailing?: boolean
}