js实现for循环1-10, 延迟500ms展示结果, 但结果总是错误的, 求办法

描述如题

代码如下

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

执行结果

clipboard.png

请教下解决办法, 并指出错误位置, 谢谢!

阅读 9.9k
16 个回答

直接在setTimeout()里用即时执行封装下回调就行了,你封包在setTimeout()的里边跟没封区别不大:

function test() {
  for(var i=1; i<11; i++) {
    setTimeout((function(){
        console.log(i);
    })(), 500)
  }
}
test();

补充:

如果想做成间隔500ms一次console.log,那我觉得不如直接写成:

var i = 1;
var ii = setInterval(function(){
    if (i<11) {
        console.log(i);
        i++;
    } else {
        clearInterval(ii);
    }
}, 500);

题主之所以会出现此问题,完全是由于没有理解闭包

es5之前js是没有块级作用域这个说法的

可以通过()()来模拟块级作用域

for(var i = 0; i < 10; i++) {
    setTimeout((function(){
        console.log(i)
    })(),500)
}
// 0 1 2 3 4 5 6 7 8 9

es6通过let命令,声明变量只在书写的let命令代码块内有效
所以 @radius 写的就复杂了

for(let i=0; i<10; i++) {
    setTimeout(function(){
        console.log(i);
    }, 500)
}
// 0 1 2 3 4 5 6 7 8 9

阮一峰es6块级作用域

es2016 or typescript:

async function test() {
    var i = 0;
    for (i = 0; i < 10; i++) {
        await new Promise(resolve => setTimeout(resolve, 500));
        console.log(i);
    }
}

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


这个是循环0-9   1-10你自己改

function test() {
for(var i = 1 ;i<=10;i++){

 (function(i){
     setTimeout(function(){
         console.log(i)
     },500)
 })(i);

}
}
test();

function test(){

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

}

test();

新手上路,请多包涵
function test() {
  for(var i=1; i<=10; i++) {
    setTimeout((function(m){
      return function () {
        console.log(m);
      }
    })(i), 500 * i)
  }
}
test();
function count() {
    var arr = [];
    for (var i=1; i<=3; i++) {
        arr.push(function () {
            return i * i;
        });
    }
    return arr;
}

var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];

在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都添加到一个Array中返回了。

你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果是:

f1(); // 16
f2(); // 16
f3(); // 16

返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
廖雪峰教程:闭包

function test() {
  for(let i=0; i<10; i++) {
    setTimeout(function(){
      (function (m) {
        console.log(m);
      })(i)
    }, 500)
  }
}
test();

只把var改成let就行了

setTimeout上修改:

setTimeout(function(m) {
    console.log(m);
})(i), 500);

错的原因是对JS变量提升的概念理解不透彻

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

换成let就好,每次循环都创建一个块级作用域

用递归来代替循环

function delayLoop(factory, time, count, _i = 0, _timer = []) {
    if(_i >= count) return _timer
    var timer = setTimeout(() => {
        _timer[_i] = undefined
        factory(_i)
        _i ++
        delay(factory, time, count, _i, _timer)
    }, time)
    _timer.push(timer)
    return _timer
}

// usage
var count = 10
var interval = 1000
var timer = delayLoop((i) => {
  console.log(i)
}, interval, count)
// stop
setTimeout(() => timer.filter(item => !!item).forEach(t => clearTimeout(t)), 3200)
for(let i = 0; i < 10; i++) {
    setTimeout(function(){
        console.log(i)
    },500)
}

最好的办法就是直接用let就好了

而下边这种写法

function test() {
  for(var i=1; i<11; i++) {
    setTimeout((function(){
        console.log(i);
    })(), 500)
  }
}

相当于

function test() {
  for(var i=1; i<11; i++) {
    setTimeout(console.log(i), 500)
  }
}

是不会有延时的。

除非:

function test() {
  for(var i=1; i<11; i++) {
    setTimeout((function(i){
        return function() {
            console.log(i);
        } 
    })(i), 500)
  }
}

明白了了吧,但是还是用let吧,骚年!

新手上路,请多包涵
for(let i=0; i<10; i++) {
    setTimeout(function(){
        console.log(i);
    }, 500*i)
}
推荐问题
宣传栏