Skip to content

noLoopFunc (JavaScript)

Language JavaScript (and super languages)
biome.json
{
"linter": {
"rules": {
"suspicious": {
"noLoopFunc": "error"
}
}
}
}

Disallow functions created inside loops when they use variables that can change.

A function created in a loop can run after the loop has moved to a later iteration. If the function reads a variable declared outside it, the function may observe the variable’s later value instead of the value from the iteration that created the function.

The rule ignores an immediately invoked function expression (IIFE), which is called as soon as it is created. It still reports async or generator IIFEs, and IIFEs that refer to their own name, because those functions can outlive the current iteration.

A var iteration variable is shared by every iteration, so a function created in the loop can observe a later value.

for (var i = 0; i < 10; i++) {
handlers.push(() => i);
}
code-block.js:2:19 lint/suspicious/noLoopFunc ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

⚠ This function declared in a loop contains unsafe references to outer variables.

1 │ for (var i = 0; i < 10; i++) {
> 2 │ handlers.push(() => i);
│ ^^^^^^^
3 │ }
4 │

ℹ Loop-created functions often run after the loop continues, but captured outer bindings like var variables are reused instead of copied per iteration.

ℹ The following variables were detected: i

ℹ Move the function outside the loop, capture only per-iteration bindings, or avoid mutating the captured variable across iterations.

let value = 0;
for (let i = 0; i < 10; i++) {
queue.push(function () {
return value;
});
value += 1;
}
code-block.js:3:16 lint/suspicious/noLoopFunc ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

⚠ This function declared in a loop contains unsafe references to outer variables.

1 │ let value = 0;
2 │ for (let i = 0; i < 10; i++) {
> 3 │ queue.push(function () {
│ ^^^^^^^^^^^^^
> 4 │ return value;
> 5 │ });
│ ^
6 │ value += 1;
7 │ }

ℹ Loop-created functions often run after the loop continues, but captured outer bindings like var variables are reused instead of copied per iteration.

ℹ The following variables were detected: value

ℹ Move the function outside the loop, capture only per-iteration bindings, or avoid mutating the captured variable across iterations.

A let or const iteration variable is created separately for each iteration, so the function keeps the expected value.

for (let i = 0; i < 10; i++) {
handlers.push(() => i);
}
for (var i = 0; i < 10; i++) {
const current = i;
queue.push(function() {
return current;
});
}