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.