Deeply Nested Callbacks
ID |
javascript.nested_callbacks |
Severity |
low |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Complexity |
Language |
JavaScript |
Tags |
async, code-style, complexity |
Description
Reports a callback function (a function literal passed as an argument) whose nesting depth exceeds the configured threshold (default 5). The depth counts callback ancestors:
fetch(url, (res) =>
parse(res, (data) =>
transform(data, (out) =>
persist(out, (id) =>
notify(id, (ok) =>
cleanup(ok, () => { // FLAW (depth 6 with limit 5)
// ...
}))))))
Rationale
Deeply nested callbacks — the original "callback hell" — are difficult to read, hard to error-handle correctly (the wrong callback receives the wrong error), and hard to refactor without breaking control flow. Modern code has two clean fixes:
-
async/await, which flattens a Promise chain into linear code, -
extracting named callback helpers, which compresses the visible nest into a series of well-named function calls.
Remediation
Use async/await:
async function pipeline() {
const res = await fetch(url);
const data = await parse(res);
const out = await transform(data);
const id = await persist(out);
const ok = await notify(id);
await cleanup(ok);
}
Or extract:
fetch(url, onFetched);
function onFetched(res) { parse(res, onParsed); }
function onParsed(data) { transform(data, onTransformed); }
// ...