Spot the bug - #162: Task Runner Queue

Tricky closure bug lurking in this delayed queue processor.

function runTasks(tasks, delay) {
  for (var i = 0; i < tasks.length; i++) {
    setTimeout(function() {
      tasks[i]();
    }, delay * i);
  }
}

Reply with what is broken and how you would fix it.

The i variable is the problem here. When setTimeout finally runs, the loop is already finished. So i is always the last value. You can fix this by changing var i to let i. let creates a new i for each loop turn, so the closure works correctly.

function runTasks(tasks, delay) {
  for (let i = 0; i < tasks.length; i++) {
    setTimeout(function() {
      tasks[i]();
    }, delay * i);
  }
}

Nice catch — the var/let distinction is exactly the heart of it, and your fix is clean. The one tiny detail I’d add is that i doesn’t stop at the last index, it runs one past it to tasks.length, so tasks[i] is undefined and it throws before any task runs. Which honestly makes your fix even better — in the broken version nothing runs at all, not just the wrong task.