Core API

safe.allSettled

Runs multiple safe-wrapped async operations in parallel and returns all individual results as named SafeResult entries. Never fails at the group level.


Signature

safe.allSettled<T extends Record<string, Promise<SafeResult<any, any>>>>(
  promises: T
): Promise<{ [K in keyof T]: Awaited<T[K]> }>

Accepts an object map of Promise<SafeResult> entries. Always returns an object where each key maps to its individual SafeResult — there is no group-level error wrapper.


Basic usage

const results = await safe.allSettled({
  user: safe.async(() => fetchUser(userId)),
  posts: safe.async(() => fetchPosts(userId)),
})

if (results.user.ok) {
  console.log(results.user.value) // User
}

if (!results.posts.ok) {
  console.error(results.posts.error) // Error
}

Mixed success and failure

Unlike safe.all, failures don't affect other results. Each operation is independent.

const results = await safe.allSettled({
  config: safe.async(() => loadConfig()),
  user: safe.async(() => fetchUser(userId)),
  analytics: safe.async(() => trackEvent('page_view')),
})

// analytics failed, but config and user may have succeeded
if (!results.analytics.ok) {
  console.warn('Analytics failed:', results.analytics.error)
}

if (results.config.ok) {
  applyConfig(results.config.value)
}

Tagged result properties

Each result is a full SafeResult with tagged properties (.ok, .value, .error) and tuple access ([0], [1]):

const results = await safe.allSettled({
  val: safe.async(() => Promise.resolve(42)),
})

// Tagged property access
results.val.ok     // true
results.val.value  // 42
results.val.error  // null

// Tuple destructuring
const [value, error] = results.val // [42, null]

With per-operation error mapping

Each operation can use its own parseError, hooks, and configuration:

const results = await safe.allSettled({
  user: safe.async(
    () => fetchUser(userId),
    (e) => ({ code: 'USER_ERROR', message: String(e) })
  ),
  posts: safe.async(
    () => fetchPosts(userId),
    (e) => ({ code: 'POSTS_ERROR', message: String(e) })
  ),
})

if (!results.user.ok) {
  console.error(results.user.error.code) // 'USER_ERROR'
}

With createSafe

On a createSafe instance, allSettled accepts raw async functions instead of pre-wrapped Promise<SafeResult> entries. The instance applies its own parseError, hooks, retry, and timeout to each function automatically.

const appSafe = createSafe({
  parseError: (e) => ({
    code: 'ERR',
    message: e instanceof Error ? e.message : 'Unknown',
  }),
  defaultError: { code: 'ERR', message: 'Unknown' },
})

const results = await appSafe.allSettled({
  user: () => fetchUser(userId),
  posts: () => fetchPosts(userId),
})

if (results.user.ok) {
  console.log(results.user.value)
}

if (!results.posts.ok) {
  console.error(results.posts.error.code) // typed as { code: string; message: string }
}

Standalone vs createSafe

The standalone safe.allSettled accepts Promise<SafeResult> entries (e.g., safe.async(() => ...)). The createSafe instance allSettled accepts raw async functions and wraps them automatically with the instance's configuration.


Empty input

Passing an empty object returns {}:

const results = await safe.allSettled({})
// results = {}

When to use safe.allSettled vs safe.all

Use safe.allSettled when you want to handle each result independently (e.g., loading optional widgets where some can fail gracefully). Use safe.all when all operations must succeed for the result to be useful.

Previous
safe.all