破解口訣:同步優(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。