setTimeout(fn,0)的解釋和應用
前言
關於setTimeout,我之前的文章介紹過。setTimeout我們經常用來做延遲執行,那麼setTimeout(fn,0),是不是就不延遲執行了!答案肯定是否定的。這篇文章,我詳細介紹一下setTimeout(fn,0)及其應用。
案例分析
for迴圈中有setTimeout,是我們在閉包案例中經常遇到和解決的一個問題,看一下下面這個案例
for(var i =0; i <3; i++){
setTimeout(function(){
console.log(i);},0);
console.log(i);}
對於上面的這個題目中的for迴圈和setTimeout結合,我之前文章中好像有寫過,但是記不清在哪裡寫的了!這個題目的列印結果是什麼呢?
分析
對於這個題目,我們可以這麼考慮,先把for迴圈裡面的內容抽出來
setTimeout(function(){
console.log(i);},0);
console.log(i);
對於這個,肯定是先執行 console.log(i),後執行setTimeout。那麼,我們把上面的for迴圈拆解開來,如下:
var i =0;
setTimeout(function(){
console.log(i);},0);
console.log(i);
i++;
setTimeout(function(){
console.log(i);},0);
console .log(i);
i++;
setTimeout(function(){
console.log(i);},0);
console.log(i);
i++;
因為setTimeout是註冊事件,要等到當前指令碼的同步任務和“任務佇列”中已有的事件,全部處理完以後,才能執行。因此,上面執行結果是把所有的console都執行完畢之後才能執行setTimeout,因此,執行結果如下:
012333
for迴圈和setTimeout
這個我之前文章中好像有提及過,針對這種情況,我們將上面案例如下修改,就可以將setTimeout也可以輸出1,2,3.
我們將setTimeout裡面的匿名函式修改成自呼叫匿名函式
function(){
console.log(i);}
修改成
(function(n){
console.log(n);})(i)
因為setTimeout的第一個引數必須是fn,因此,我們要對這個匿名函式返回一個function
(function(n){returnfunction(){
console.log(n);};})(i)
那麼,程式碼如下:
for(var i =0; i <3; i++){
setTimeout((function(n){returnfunction(){
console.log(n);};})(i),0);
console.log(i);}
這樣執行結果就是
012012
應用
我們看下面的執行結果
console.log(1);
setTimeout(function(){
console.log(2);},0);Promise.resolve().then(function(){
console.log(3);}).then(function(){
console.log(4);});
console.log(5);// 1// 5// 3// 4// 2
上面程式碼的執行結果說明,setTimeout(fn, 0)在Promise.resolve之後執行。這是因為setTimeout語句指定的是“正常任務”,即不會在當前的Event Loop(事件迴圈)執行。而Promise會將它的回撥函式,在狀態改變後的那一輪Event Loop(事件迴圈)指定為微任務。所以,3和4輸出在5之後、2之前。
setTimeout(fn, 0)的一大應用是,可以調整事件的發生順序。比如,網頁開發中,某個事件先發生在子元素,然後冒泡到父元素,即子元素的事件回撥函式,會早於父元素的事件回撥函式觸發。如果,我們先讓父元素的事件回撥函式先發生,就要用到setTimeout(fn, 0)。
案例如下:
document.getElementById("haoroomsID").onclick =function A(){
setTimeout(function B(){
console.log("觸發子元素事件")},0)};
document.body.onclick =function C(){
console.log("觸發父元素事件")};
上面案例,點選haoroomsID會先觸發父級元素事件,然後再觸發子元素事件。