for(var i = 0; i < 5; i++) { setTimeout(function() { console.log(new Date, i) }, 1000) } console.log(new Date, i)
很明显,因为异步的做用。到最后输出的结果为6个5javascript
若是用箭头表示先后两次输出有1s的间隔,用,表明先后一块儿输出,那么输出结果是5->5,5,5,5,5
java
这个也很容易的就能够进行解释,先执行console.log()
,再进行setTimeout()
的异步操做。闭包
首先可使用闭包来解决这个问题:异步
for(var i = 0; i < 5; i++) { (function(j) { setTimeout(function() { console.log(new Date, j) }, 1000) })(i) } console.log(new Date, i) // 5
利用当即执行函数,来解决闭包形成的问题。async
此外还可使用setTimeout
的第三个参数 文档:函数
for(var i = 0; i < 5; i++) { setTimeout(function(j) { console.log(new Date, j) }, 1000, i) } console.log(new Date, i) // 5
可能会有不少同窗采用ES6的方式来避免:code
for(let i = 0; i < 5; i++) { setTimeout(function() { console.log(new Date, i) }, 1000) } console.log(new Date, i)
可是此时并不能正确输出咱们想要的结果,由于let声明的 i 产生了块级做用域,致使 for 循环外面的输出不能获取的 i ,因此此时会报错 i is not defined
ip
其中一种比较容易想到的方法:作用域
for(var i = 0; i < 5; i++) { (function(j) { setTimeout(function() { console.log(new Date, j) }, 1000*j) })(i) } setTimeout(function() { console.log(new Date, i) }, 1000*i)
可是js中定时器的触发时机是不肯定的,每次循环都会产生一个异步操做以后,会有一个输出,那么彻底可使用Promise
来解决这个问题。文档
const tasks = [] for(var i = 0; i < 5; i++) { (function(j){ tasks.push(new Promise((resolve) => { setTimeout(() => { console.log(new Date, j) resolve() }, j*1000) })) })(i) } Promise.all(tasks).then( () => { setTimeout( () => { console.log(new Date, i) }, 1000) })
将上面代码处理一下:
const tasks = [] const output = function(i) { new Promise( (resolve) => { setTimeout( () => { console.log(new Date, i) resolve() }, 1000*i) }) } for(var i = 0;i < 5; i++) { tasks.push(output(i)) } // 所有Promise执行完毕,执行最后一个输出i Promise.all(tasks).then( () => { setTimeout( () => { console.log(new Date, i) }, 1000) })
async / await
怎么实现// 模拟sleep const sleep = (time) => new Promise((resolve) => { setTimeout(resolve, time); }); (async () => { // 声明即执行的 async 函数表达式 for (var i = 0; i < 5; i++) { if (i > 0) { await sleep(1000); } console.log(new Date, i); } await sleep(1000); console.log(new Date, i); })();