答案是丰富多彩的系列5:js实现循环setTimeout输出0,1,2,3,4

时间:17-04-25 栏目:ES6[ES2015], Javascript 作者:zongyan86 评论:0 点击: 7,759 次

这是js比较常见的一个题目:

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

结果为:

Tue Apr 25 2017 14:50:46 GMT+0800 (中国标准时间) 5
Tue Apr 25 2017 14:50:46 GMT+0800 (中国标准时间) 5
Tue Apr 25 2017 14:50:46 GMT+0800 (中国标准时间) 5
Tue Apr 25 2017 14:50:46 GMT+0800 (中国标准时间) 5
Tue Apr 25 2017 14:50:46 GMT+0800 (中国标准时间) 5

而不是我们所期望的

Tue Apr 25 2017 14:50:46 GMT+0800 (中国标准时间) 0
VM595:4 Tue Apr 25 2017 14:50:46 GMT+0800 (中国标准时间) 1
VM595:4 Tue Apr 25 2017 14:50:46 GMT+0800 (中国标准时间) 2
VM595:4 Tue Apr 25 2017 14:50:46 GMT+0800 (中国标准时间) 3
VM595:4 Tue Apr 25 2017 14:50:46 GMT+0800 (中国标准时间) 4

解决方法:

第一种:闭包

for (var i = 0; i < 5; i++) {
    (function(j) {  // j = i
        setTimeout(function() {
            console.log(new Date, j);
        }, 1000);
    })(i);
}

第二种:值类型传递

var output = function (i) {
    setTimeout(function() {
        console.log(new Date, i);
    }, 1000);
};

for (var i = 0; i < 5; i++) {
    output(i);  // 这里传过去的 i 值被复制了
}

console.log(new Date, i);

第三种:es6 let

for (let i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(new Date, i);
    }, 1000);
}

那如果要让0-4一秒一秒地输出来呢?

第一种:

for (var i = 0; i < 5; i++) {
    (function(j) {
        setTimeout(function() {
            console.log(new Date, j);
        }, 1000 * j);  // 这里修改 0~4 的定时器时间
    })(i);
}

setTimeout(function() { // 这里增加定时器,超时设置为 5 秒
    console.log(new Date, i);
}, 1000 * i);

第二种:

const tasks = [];
for (var i = 0; i < 5; i++) {   // 这里 i 的声明不能改成 let,如果要改该怎么做?
    ((j) => {
        tasks.push(new Promise((resolve) => {
            setTimeout(() => {
                console.log(new Date, j);
                resolve();  // 这里一定要 resolve,否则代码不会按预期 work
            }, 1000 * j);   // 定时器的超时时间逐步增加
        }));
    })(i);
}

Promise.all(tasks).then(() => {
    setTimeout(() => {
        console.log(new Date, i);
    }, 1000);   // 注意这里只需要把超时设置为 1 秒
});

第三种:

const tasks = []; // 这里存放异步操作的 Promise
const output = (i) => new Promise((resolve) => {
    setTimeout(() => {
        console.log(new Date, i);
        resolve();
    }, 1000 * i);
});

// 生成全部的异步操作
for (var i = 0; i < 5; i++) {
    tasks.push(output(i));
}

// 异步操作完成之后,输出最后的 i
Promise.all(tasks).then(() => {
    setTimeout(() => {
        console.log(new Date, i);
    }, 1000);
});

第四种:

// 模拟其他语言中的 sleep,实际上可以是任何异步操作
const sleep = (timeountMS) => new Promise((resolve) => {
    setTimeout(resolve, timeountMS);
});

(async () => {  // 声明即执行的 async 函数表达式
    for (var i = 0; i < 5; i++) {
        await sleep(1000);
        console.log(new Date, i);
    }

    await sleep(1000);
    console.log(new Date, i);
})();

web开发分享



声明: 本文由( zongyan86 )原创编译,转载请保留链接: 答案是丰富多彩的系列5:js实现循环setTimeout输出0,1,2,3,4

关注我们