Skip to content

Instantly share code, notes, and snippets.

@dmh2000
Last active September 17, 2020 12:43
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dmh2000/c946354c97f424330a96 to your computer and use it in GitHub Desktop.
Save dmh2000/c946354c97f424330a96 to your computer and use it in GitHub Desktop.
javascript loop with async callback handling
"use strict";
// =================================================
// a common question that pops up is
// 'why do my async functions use the final value of my loop variable instead of the one they are called with'?
// its because they refer directly to the loop variable and its last value
// the solutions are various ways to bind the loop variable in a new scope
//
// this gist shows different ways to handle a loop that spawns an async function that depends on the loop index
// =================================================
// if you run this whole file it will print 40 40 40 40 40
// if you run this by itself it will print 5 5 5 5 5
for(var i=0;i<5;++i) {
setTimeout(function() {
console.log(i);
},
500);
}
// first a couple of supporting functions
// this is good because 'this' could be any object
function f() {
console.log(this);
}
// return a function that captures the parameter i
function a(i) {
return function() {
console.log(i);
}
}
// now the loops
// bind the variable as the context 'this'
for(var i=5;i<10;++i) {
setTimeout(f.bind(i) // is this a questionable practice? I'm not sure
,1500);
}
// same as f() but anonymous
for(var i=10;i<15;++i) {
setTimeout(
function() {
console.log(this);
}.bind(i)
,2000);
}
// call a separate function that returns a function
// which binds the parameter
for(var i=15;i<20;++i) {
setTimeout(
a(i)
,3500);
}
// same as function a(i) but anonymous IIFE
// note that in this context the surrounding parens usually required for an IIFE
// are not needed because the function parameter is an expression
for(var i=20;i<25;++i) {
setTimeout(
function(j) {
return function() {
console.log(j);
}
}(i)
,4000);
}
// same as before but anonymous IIFE with the usual parens
for(var i=25;i<30;++i) {
setTimeout(
(function(j) {
return function() {
console.log(j);
}
})(i)
,4500);
}
// =======================================================
// comment the rest out if you don't have es6 (node 4)
// =======================================================
// 'let' effectively creates a new instance of i in each iteration
for(let i=30;i<35;++i) {
setTimeout(
function() {
console.log(i);
}
,5000);
}
// simiarl to IIFE above but with arrow functions
// in this case the parens surrounding the IIFE seem to be required at least in node 4.1
for(var i=35;i<40;++i) {
setTimeout(
(j => {
return () => {
console.log(j);
};
})(i)
,5500);
}
@steelbrain
Copy link

I think I've blogged about this months ago https://thewebsmith.co/blog/beauty-of-es6-let_7

@jhuckaby
Copy link

jhuckaby commented Oct 7, 2015

Here is how I've solved this in the past, just add a self-calling function, and pass i into it.

for(var i=0;i<5;++i) {
    (function(i) {
        setTimeout(function() {
            console.log(i);
        }, 500);
    })(i);
}

Emits: 0, 1, 2, 3, 4

@threepointone
Copy link

here's another alternative

function sleep(period){
  return new Promise(resolve => setTimeout(() => resolve(), period));
}

async function run(){
  for(var i=0; i<5; i++){
    await sleep(500);
    console.log(i);
  }
}

run();

@cristea2017
Copy link

here's another alternative

function sleep(period){
  return new Promise(resolve => setTimeout(() => resolve(), period));
}

async function run(){
  for(var i=0; i<5; i++){
    await sleep(500);
    console.log(i);
  }
}

run();

thanks man , you saved me 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment