Resilience
The @daiso-tech/core/resilience component provides predefined fault tolerant middlewares.
For further information about middlewares refer to @daiso-tech/core/hooks documentation.
Fallback
The fallback middleware adds fallback value when an error occurs:
Usage
import { fallback } from "@daiso-tech/core/resilience";
import { AsyncHooks } from "@daiso-tech/core/hooks";
function unstableFn(): number {
// We simulate a function that can throw unexpected errors
if (Math.round(Math.random() * 1.5) === 0) {
throw new Error("Unexpected error occured");
}
return Math.round((Math.random() + 1) * 99);
}
const fn = new AsyncHooks(unstableFn, [
fallback({
fallbackValue: 1,
}),
]);
// Will never throw and when error occurs the fallback value will be returned.
console.log(await fn.invoke());
Custom ErrorPolicy
You can define an ErrorPolicy to specify fallback values for specific error cases:
const fn = new AsyncHooks(unstableFn, [
fallback({
fallbackValue: 1,
// Will only fallback errors that are not a TypeError
errorPolicy: (error) => !(error instanceof TypeError),
}),
]);
Callbacks
You can add callback Invokable that will be called before the fallback value is returned.
const fn = new AsyncHooks(unstableFn, [
fallback({
fallbackValue: 1,
onFallback: (fallbackData) => console.log(fallbackData),
}),
]);
For more details about onFallback callback data, see the OnFallbackData type.
Retry
The retry middleware enables automatic retries for all errors or specific errors, with configurable backoff policies. An error will be thrown when all retry attempts fail.
Usage
import { retry } from "@daiso-tech/core/resilience";
import { AsyncHooks } from "@daiso-tech/core/hooks";
function unstableFn(): number {
// We simulate a function that can throw unexpected errors
if (Math.round(Math.random() * 1.5) === 0) {
throw new Error("Unexpected error occured");
}
return Math.round((Math.random() + 1) * 99);
}
const fn = new AsyncHooks(unstableFn, [
retry({
// Will retry 4 times
maxAttemps: 4,
}),
]);
await fn.invoke();
Custom ErrorPolicy
You can define an ErrorPolicy to retry specific error cases:
const fn = new AsyncHooks(unstableFn, [
retry({
maxAttemps: 4,
// Will only retry errors that are not TypeError
errorPolicy: (error) => !(error instanceof TypeError),
}),
]);
Custom BackoffPolicy
You can use custom BackoffPolicy:
import { TimeSpan } from "@daiso-tech/core/time-span";
const fn = new AsyncHooks(unstableFn, [
retry({
maxAttemps: 4,
// By default a exponential policy is used
backoffPolicy: (attempt: number, _error: unknown) =>
TimeSpan.fromMilliseconds(attempt * 100),
}),
]);
Callbacks
You can add callback Invokable that will be called before execution attempt:
const fn = new AsyncHooks(unstableFn, [
retry({
maxAttemps: 4,
onExecutionAttempt: (data) => console.log(data),
}),
]);
You can add callback Invokable that will be called before the retry delay starts:
For more details about onExecutionAttempt callback data, see the OnRetryAttemptData type.
const fn = new AsyncHooks(unstableFn, [
retry({
maxAttemps: 4,
onRetryDelay: (data) => console.log(data),
}),
]);
For more details about onRetryDelay callback data, see the OnRetryDelayData type.
Timeout
The timeout middleware automatically aborts functions after a specified time period, throwing an error when aborted.
Usage
import { timeout } from "@daiso-tech/core/resilience";
import { AsyncHooks } from "@daiso-tech/core/hooks";
import { TimeSpan } from "@daiso-tech/core/time-span";
function fetchData(): Promise<Response> {
const response = await fetch("ENDPOINT");
console.log("DONE");
return response;
}
const fn = new AsyncHooks(fetchData, [
timeout({
waitTime: TimeSpan.fromSeconds(2),
}),
]);
await fn.invoke();
Note when a timeout occurs, the function call continues executing in the background and only the Promise will be aborted.
To ensure correct abortion behavior, provide an AbortSignalBinder to AsyncHooks.
For further information about AbortSignalBinder and AsyncHooks refer to @daiso-tech/core/hooks documentation.
import { timeout } from "@daiso-tech/core/resilience";
import { TimeSpan } from "@daiso-tech/core/time-span";
import { AsyncHooks } from "@daiso-tech/core/hooks";
function fetchData(signal?: AbortSginal): Promise<Response> {
const response = await fetch("ENDPOINT", {
signal,
});
console.log("DONE");
return response;
}
const fn = new AsyncHooks(
fetchData,
[
timeout({
waitTime: TimeSpan.fromSeconds(2),
}),
],
{
signalBinder: {
getSignal: (args) => args[0],
forwardSignal: (args, signal) => {
args[0] = signal;
},
},
},
);
await fn.invoke();
Callbacks
You can add callback Invokable that will be called before the timeout occurs.
const fn = new AsyncHooks(
fetchData,
[
timeout({
waitTime: TimeSpan.fromSeconds(2),
onTimeout: (data) => console.log(data),
}),
],
{
signalBinder: {
getSignal: (args) => args[0],
forwardSignal: (args, signal) => {
args[0] = signal;
},
},
},
);
For more details about onTimeout callback data, see the OnTimeoutData type.
Observe
The observe middleware tracks an async function's state and runs callbacks when it fails with an error or succeeds:
Usage
import { observe } from "@daiso-tech/core/resilience";
import { AsyncHooks } from "@daiso-tech/core/hooks";
function unstableFn(): Promise<number> {
// We simulate a function that can throw unexpected errors
if (Math.round(Math.random() * 1.5) === 0) {
throw new Error("Unexpected error occured");
}
return Math.round((Math.random() + 1) * 99);
}
const fn = new AsyncHooks(unstableFn, [
observe({
onStart: (data) => console.log("START:", data),
onSuccess: (data) => console.log("SUCCESS:", data),
onError: (data) => console.error("ERROR:", data),
onFinally: (data) => console.log("END:", data),
}),
]);
await fn.invoke();
-
For more details about
onStartcallback data, see theOnObserveStartDatatype. -
For more details about
onSuccesscallback data, see theOnObserveSuccessDatatype. -
For more details about
onErrorcallback data, see theOnObserveErrorDatatype. -
For more details about
onFinallycallback data, see theOnObserveFinallyDatatype.
Further information
For further information refer to @daiso-tech/core/resilience API docs.