Skip to content

Multi-Loader

The first AsyncLoader argument loader accepts an array, and the multiLoaderStrategy option decides how to orchestrate multiple loader functions: failover (fallback) or multi-source merge (combine). Multi-loader scenarios reuse the full set of capabilities: caching / abort / timeout / retry / fallback / multiplex.

fallback: failover

Try loaders in order; the first success stops the rest; on a loader failure, try the next; all failures count as one failed attempt.

typescript
import { AsyncLoader } from "asyncsignal/loader";

// Fall back to backup endpoints when the primary fails
const loader = new AsyncLoader(
    [
        async (args) => (await fetch("/api/primary", { signal: args.abortSignal })).json(),
        async (args) => (await fetch("/api/secondary", { signal: args.abortSignal })).json(),
        async (args) => (await fetch("/api/cdn", { signal: args.abortSignal })).json(),
    ],
    { multiLoaderStrategy: "fallback", retry: 1 }
);

const data = await loader.get(); // try in order until one succeeds

fallback does not call onResults

fallback only does "failover on error" and never calls onResults (ignored even if configured). onResults is a merge hook specific to combine.

combine: multi-source merge

All loaders run concurrently; results (successful T, failed Error) are collected and passed to onResults to merge. Uses lenient semantics (Promise.allSettled): a single loader failure does not abort the others — it just enters results as an Error.

typescript
// Fetch user info and orders concurrently and merge into one object
const loader = new AsyncLoader(
    [
        async (args) => (await fetch("/api/user", { signal: args.abortSignal })).json(),
        async (args) => (await fetch("/api/orders", { signal: args.abortSignal })).json(),
    ],
    {
        multiLoaderStrategy: "combine",
        onResults: (results, defaultValue) => {
            // Merge successful items; failed items are Error instances
            const [user, orders] = results;
            if (user instanceof Error || orders instanceof Error) {
                return defaultValue as { user: any; orders: any }; // fall back on partial failure
            }
            return { user, orders };
        },
    }
);

const { user, orders } = await loader.get();

onResults in depth

onResults(results, defaultValue) => T is the result merge hook for the combine strategy:

ArgumentDescription
resultsArray of all loaders' results (in loaders order); successful items are T, failed items are Error objects
defaultValueThe value from options.defaultValue (may be undefined), available for the hook to fall back
  • Returns the merged result T, which becomes the return value of get() and is written to the cache.
  • A synchronous throw is treated as a failed attempt (goes through the retry / onRejected / defaultValue terminal flow).
  • This is a transformation function with a return value (not an event callback) — errors must propagate, and it does not go through the error-swallowing mechanism of onFulfilled/onRejected.

combine requires onResults

When multiLoaderStrategy="combine" and there is more than one loader, you must provide onResults; otherwise construction throws: multiLoaderStrategy="combine" requires onResults.

Interaction with other capabilities

CapabilityInteraction with multi-loader
timeoutper-attempt: multiple loaders share the timeout window of the current attempt (both fallback/combine)
retryOn fallback all-failed / combine final failure (including onResults throws), the entire loaders sequence is retried according to retry
abort()Aborts the inflight loader(s); onRejected is fired synchronously by abort()
defaultValueBoth passed to onResults as an argument (for its decision) and used as the final fallback resolve on failure
multiplexOn reuse, the first instance's loaders array wins; loader(s) passed by the new instance are ignored
hashAuto-generated from the loaders array (order-sensitive: [a,b] differs from [b,a])

Single-loader backward compatibility

When a single loader (or a one-element array) is passed, multiLoaderStrategy has no effect and behavior is identical to before the change.

Released under the MIT License.