JavaScript同步、異步、回調執(zhí)行順序之經(jīng)典setTimeout面試題分析

破解口訣:同步優(yōu)先、異步靠邊、回調墊底

for (var i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log('i--- ',i);
    }, 1000);
}
console.log(i);
//輸出
5
i---  5
i---  5
i---  5
i---  5
i---  5

為什么是輸出是這樣呢?下面我給大家分析一下
1、for循環(huán)和循環(huán)外的console是同步的,所以先執(zhí)行for循環(huán),再執(zhí)行外部的console.log。(同步優(yōu)先)
2、for循環(huán)里面有一個setTimeout回調,他是墊底的存在,只能最后執(zhí)行。(回調墊底)
分析:
for循環(huán)先執(zhí)行,但是不會給setTimeout傳參(回調墊底),等for循環(huán)執(zhí)行完,就會給setTimeout傳參,而外部的console打印出5是因為for循環(huán)執(zhí)行完成了。
內部的setTimeout回調執(zhí)行的時候 因為for循環(huán)已經(jīng)執(zhí)行完了 所以這個時候i=5 給setTimeout傳參(5) 所以setTimeout內部的console.log輸出的就是 5;

下面我們加一行代碼仔細看一下這個輸出的過程:

for (var i = 0; i < 5; ++i) {
    setTimeout(function() {
        console.log('s---',i);
    }, 1000);
    console.log('c---', i); //新加一行代碼
}
console.log(i);
 
//輸出
c---  0
c---  1
c---  2
c---  3
c--- 4
5 //console.log(i);輸出的
s---  5
s--- 5
s---  5
s---  5
s---  5

這個時候面試官也許會問你,怎么才能正常輸出0,1,2,3,4呢?
下面給出兩個方法:

方法一:
for (let i = 0; i < 5; ++i) {
    setTimeout(function() {
        console.log('s--- ',i);
    }, 1000);
}
 
console.log(i);
 
//輸出
i is not defined
s---   0
s---   1
s---   2
s---   3
s---   4

分析:
我們來分析一下,用了let作為變量i的定義之后,
for循環(huán)每執(zhí)行一次,都會先給setTimeout傳參,
準確的說是給loop傳參,loop形成了一個閉包,
這樣就執(zhí)行了5個loop,每個loop傳的參數(shù)分別是0,1,2,3,4
然后loop里面的setTimeout會進入消息隊列排隊等候。
當外部的console執(zhí)行完畢,因為for循環(huán)里的i變成了
一個新的變量 _i ,所以在外部的console.log(i)是不存在的。

方法二:
var loop = function (_i) {
    setTimeout(function() {
        console.log('2:', _i);
    }, 1000);
};
 
for (var _i = 0; _i < 5; _i++) {
    loop(_i);
}
 
console.log(i);

上面主要講了同步和回調執(zhí)行順序的問題,接著我就舉一個包含同步、異步、回調的例子。

let a = new Promise(
  function(resolve, reject) {
    console.log(1)
    setTimeout(() => console.log(2), 0)
    console.log(3)
    console.log(4)
    resolve(true)
  }
)
a.then(v => {
  console.log(8)
})
 
let b = new Promise(
  function() {
    console.log(5)
    setTimeout(() => console.log(6), 0)
  }
)
console.log(7)

口訣最重要 回憶一下(同步-異步-回調)
下面我們來分析一下:

1.看同步代碼:a變量是一個Promise,我們知道Promise是異步的,
是指他的then()和catch()方法,Promise本身還是同步的,
所以這里先執(zhí)行a變量內部的Promise同步代碼。(同步優(yōu)先)
2、Promise內部有4個console,第二個是一個setTimeout回調
(回調墊底)。所以這里先輸出1,3,4回調的方法丟到消息隊列中
排隊等著。
3、接著執(zhí)行resolve(true),進入then(),then是異步,
下面還有同步?jīng)]執(zhí)行完呢,所以then也去消息隊列排隊等候
(異步靠邊)
4、b變量也是一個Promise,和a一樣,執(zhí)行內部的同步代碼,
輸出5,setTimeout滾去消息隊列排隊等候。
5、最下面同步輸出7。
6、同步的代碼執(zhí)行完了,JavaScript就跑去消息隊列呼叫異步的
代碼:異步,出來執(zhí)行了。這里只有一個異步then,所以輸出8。
7、異步也over,輪到回調的孩子們:回調,出來執(zhí)行了。這里有
2個回調在排隊,他們的時間都設置為0,所以不受時間影響,
只跟排隊先后順序有關。則先輸出a里面的回調2,最后輸出b里面
的回調6。(這里 如果時間不一樣的話 執(zhí)行順序就要根據(jù)時間先
后來輸出)
8、最終輸出結果就是:1、3、4、5、7、8、2、6。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容