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); })();