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

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

1. 不起眼的開始

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

很明顯,由于異步的作用。到最后輸出的結(jié)果為6個5

如果用箭頭表示前后兩次輸出有1s的間隔,用,代表前后一起輸出,那么輸出結(jié)果是5->5,5,5,5,5

這個也很容易的就可以進(jìn)行解釋,先執(zhí)行console.log(),再進(jìn)行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

利用立即執(zhí)行函數(shù),來解決閉包造成的問題。

此外還可以使用setTimeout的第三個參數(shù) 文檔

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

可能會有很多同學(xué)采用ES6的方式來避免:

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

但是此時并不能正確輸出我們想要的結(jié)果,因為let聲明的 i 產(chǎn)生了塊級作用域,導(dǎo)致 for 循環(huán)外面的輸出不能獲取的 i ,所以此時會報錯 i is not defined

追問2:如果把輸出變?yōu)?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中定時器的觸發(fā)時機(jī)是不確定的,每次循環(huán)都會產(chǎn)生一個異步操作之后,會有一個輸出,那么完全可以使用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執(zhí)行完畢,執(zhí)行最后一個輸出i
Promise.all(tasks).then( () => {
    setTimeout( () => {
        console.log(new Date, i)
    }, 1000)
})

追問3:使用 async / await 怎么實現(xiàn)

// 模擬sleep
const sleep = (time) => new Promise((resolve) => {
    setTimeout(resolve, time);
});

(async () => {  // 聲明即執(zhí)行的 async 函數(shù)表達(dá)式
    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);
})();
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時...
    歐辰_OSR閱讀 30,228評論 8 265
  • 問題 一、什么是閉包? 有什么作用? 閉包閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。在javascript中,只有函...
    婷樓沐熙閱讀 656評論 0 0
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML標(biāo)準(zhǔn)。 注意:講述HT...
    kismetajun閱讀 28,804評論 1 45
  • 【編者按】本文作者為來自南非約翰內(nèi)斯堡的女程序員 Rebecca Franks,Rebecca 熱衷于安卓開發(fā),擁...
    OneAPM閱讀 1,259評論 0 2
  • “本文參加#山南杯短篇小說大賽#活動,本人承諾,文章為原創(chuàng),且未在其他平臺發(fā)表過” 她餓極了,求生的本能讓她一下車...
    篁溫閱讀 228評論 0 0

友情鏈接更多精彩內(nèi)容