Skip to content

no-hand-rolled-sleep

Disallow uncancellable promisified timers and timeout arms.

Why

A timer that outlives an aborted operation or a lost promise race retains work and can keep the process alive until it fires.

Fix

Use node:timers/promises with an abort signal for delays, or pass AbortSignal.timeout(...) to the timed operation.

Examples

Before — flagged A Promise wraps a timer without cancellation
src/lib/queue.ts
await new Promise((resolve) => setTimeout(resolve, 500));
After — preferred A standard-library timer accepts an abort signal
src/lib/queue.ts
import { setTimeout as sleep } from "node:timers/promises";
await sleep(500, undefined, { signal });

Options

{
"additionalProperties": false,
"properties": {
"allowIn": {
"description": "Glob patterns for modules exempt from the rule (e.g. a single sanctioned `sleep` utility). Matched against the ABSOLUTE file path, so anchor with a `**/` prefix (e.g. `**/lib/sleep.ts`).",
"items": { "type": "string" },
"type": "array"
},
"checkClientModules": {
"description": "Also report the sleep form in browser/React Native modules. Off by default: those bundles cannot import `node:timers/promises` and the web platform has no equivalent, so the fix is impossible to follow. Turn on only where every file can resolve `node:` builtins.",
"type": "boolean"
}
},
"type": "object"
}