写在前面的话
setTimeout是我们学习
JavaScript基础都必须面对的问题,也许当时你搞懂了,但是过一段时间就又忘记了。最近事情不多,我将梳理出for + setTimeout相关的知识点,以及使用Promise、async/await来加深对异步、同步的理解
setTimeout
直接进入正题:
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
也许我们希望输出的是
1,2,3,4,5,但实际情况是我们输出了5,5,5,5,5,这到底是为什么呢?
由JS的运行机制中我们得知:当线程中没有任何同步代码的前提下才会执行异步代码。我们的for循环时同步的,但是setTimeout是异步的,由此就造成了输出5,5,5,5,5这种情况。
通过一顿胡乱分析我们得出假设:只要保证里面的也变成同步的是不是就可以了呢?
方式①
for(var i = 0; i < 5; i++){
setTimeout(function(){
console.log(i);
}, i*1000)
}
这里使用的是最简单的方式:动态的改变延迟的时间。
因为for循环时同步的,而setTimeout是异步的,所以会先把for循环执行完毕,然后再执行内部的setTimeout,所以输出结果为每隔1秒输出一个5
方式②
for (var i = 0; i < 5; i++) {
(function(j){
setTimeout(function (){
console.log(j);
},1000);
})(i);
}
我们通过设置一个立即执行函数
(IIFE),这样就能保证里面和外面的是同步执行的。
方式③
function output(i){
setTimeout(function(){
console.log(i);
}, 1000)
}
for(var i = 0; i < 5; i++){
output(i);
}
这里其实和第一种方法类似,只不过我们把这个函数单独拿出来,并把
index值当做函数的参数来传递
方式④
for(let i = 0; i < 5; i++){
setTimeout(function(){
console.log(i);
}, 1000)
}
let是ES6语法,for循环代码块构成一个作用域,里面的内容引用了上层作用域的变量i,并最终形成五个闭包,而for使用var时,还是ES5的写法,for代码块没有形成作用域,所以里面的function不构成闭包。同理我们的方式①和方式②都形成了闭包函数。
如果我们需要最后一个延迟5秒,其余的都是延迟1秒,我们就可以用到ES6的语法--Promise,下面我们用Promise实现这一情况
方式⑤
const task = [];
const output = (i) => new Promise(function(resolve, reject){
setTimeout(function(){
console.log(i);
resolve();
}, i*1000)
})
for (var i = 0; i<5; i++){
task.push(output(i));
}
Promise.all(task).then(() => {
setTimeout(() => {
console.log(i);
}, 5000);
})
这里采用了
Promise解决异步的方式,在ES7中解决异步还有async/await的方式
方式⑥
const sleep = (timeount) => new Promise((resolve) => {
setTimeout(resolve, timeount);
});
Func = async () => {
for(var i = 0; i<5; i++){
await sleep(1000);
console.log(i);
}
await sleep(5000);
console.log(i);
}
this.Func();
async表示这是一个async函数,await只能用在这个函数里面;
await表示在这里等待promise返回结果了,再继续执行;
await后面跟着的应该是一个promise对象,否则没有同步效果







网友评论