從閉包引出來的一系列問題

從閉包引出來的一系列問題

1. 不起眼的開始

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,5java

這個也很容易的就能夠進行解釋,先執行console.log(),再進行setTimeout()的異步操做。閉包

追問1:若是變成 5 -> 0,1,2,3,4 該怎樣處理?

首先可使用閉包來解決這個問題:異步

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 definedip

追問2:若是把輸出變爲 0->1->2->3->4->5 呢?

其中一種比較容易想到的方法:作用域

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

追問3:使用 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);
})();
相關文章
相關標籤/搜索