Signals and cancellation
Your function receives a context with two fields:
signal, anAbortSignalthat aborts when the work should stop.attempt, the 1-based attempt number. A retry increments it.
import { Tenace } from '@julr/tenace'
const response = await Tenace.policy() .withTimeout('1s') .withRetry({ retries: 2 }) .execute(({ signal, attempt }) => { console.log(`attempt ${attempt}`) return fetch('https://example.com/users', { signal }) })Pass the signal to everything that accepts one: fetch, database drivers, child processes. A timeout cannot stop work that ignores its signal. It can only stop waiting for it.
What aborts the signal
Section titled “What aborts the signal”- A timeout. At the deadline, the timeout aborts the signal of the steps inside it. The abort reason is the
TimeoutError. - The caller. Pass
signaltoexecute()orsettle(). Aborting it aborts the signal of the function with the same reason. - A failed batch sibling. In a fail-fast batch, the first failure aborts the signal of the tasks still running. See Batches.
- Every caller of a deduplicated run leaving. The shared run of a dedupe aborts when its last caller aborts. See Dedupe.
When the caller aborts
Section titled “When the caller aborts”A caller abort stops the execution right away. execute() and settle() both reject with signal.reason, even if the function ignores its signal. The abort reason is not part of the error channel, the same way an aborted fetch rejects with an AbortError rather than returning a value.
import { Tenace } from '@julr/tenace'
const controller = new AbortController()setTimeout(() => controller.abort(new Error('user left')), 100)
try { await Tenace.policy() .withRetry({ retries: 5 }) .settle(({ signal }) => fetch('https://example.com/report', { signal }), { signal: controller.signal, })} catch (error) { console.log(error) // Error: user left}If the signal is already aborted, the policy rejects without calling the function.
The strategies react to the abort too:
- A retry stops waiting and does not start another attempt.
- A fallback does not run.
- A circuit breaker does not count the aborted call as a failure or a success.
- A call waiting in a bulkhead queue, a lock queue, or a rate limit leaves it, and does not run even if it gets its slot or its lock at that moment.