一道筆試題幫你梳理多個知識點。請寫出以下一段代碼輸出結果(用逗號隔開)
setTimeout(function(){
console.log(1);
},0)
new Promise(function executor(resolve){
console.log(2);
for(var i=0;i<1000;i++){
i=9999 && resolve();
}
console.log(3);
}).then(function(){
console.log(4);
})
console.log(5);
先給你運行後的答案 2,3,5,4,1;然後自己思考兩分鐘,你的答案是什麼?
那麼你應該有這麼個解題思路
一、js的執行機制是什麼?
先要搞清楚一個很重要的概念:js是單線程的。JS的執行機制就可以看做是一個主線程加上一個任務隊列(task queue)。同步任務就是放在主線程上執行的任務,異步任務是放在任務隊列中的任務。所有的同步任務在主線程上執行,形成一個執行棧;異步任務有了運行結果就會在任務隊列中放置一個事件;腳本運行時先依次運行執行棧,然後會從任務隊列裏提取事件,運行任務隊列中的任務,這個過程是不斷重複的,所以又叫做事件循環(Event loop)。
簡單點來說,就是先執行同步的代碼,然後在執行異步的代碼(如setTimeout、Promise,Ajax操作等);
二、setTimeout(fn,0)函數
用處就在於我們可以改變任務的執行順序!之前講過,所有的同步任務在主線程上執行,形成一個執行棧;異步任務有了運行結果就會在任務隊列中放置一個事件;腳本運行時先依次運行執行棧,然後會從任務隊列裏提取事件,運行任務隊列中的任務。
然後呢,setTimeout(fn,0)它在"任務隊列"的尾部添加一個事件,因此要等到同步任務和"任務隊列"現有的事件都處理完,纔會得到執行。所以,如下代碼:
setTimeout(function(){
console.log(1);
},0)
//暫時不分析promise
// new Promise(function executor(resolve){
// console.log(2);
//
// for(var i=0;i<1000;i++){
//
// i=9999 && resolve();
// }
//
// console.log(3);
// }).then(function(){
// console.log(4);
// })
console.log(5);
運行結果是5,1沒有問題吧!
三、認識Promise
Promise不管是面試還是做筆試題都是考察的重中之中。分爲三個問題:
(1)什麼是Promise?
Promise 是異步編程的一種解決方案,從語法上說,Promise 是一個對象,從它可以獲取異步操作的消息。Promise對象有三種狀態:pending
(進行中)、fulfilled
(已成功)和rejected
(已失敗);一旦狀態改變,就不會再變,任何時候都可以得到這個結果;
(2)爲什麼要用Promise?
有了Promise
對象,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。此外,Promise
對象提供統一的接口,使得控制異步操作更加容易。
(3)Promise怎麼用?
Promise
構造函數接受一個函數作爲參數,該函數的兩個參數分別是resolve
和reject
。resolve
函數在異步操作成功時調用,並將異步操作的結果,作爲參數傳遞出去;對應的,reject
函數的作用是,在異步操作失敗時調用,並將異步操作報出的錯誤,作爲參數傳遞出去。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 異步操作成功 resolve函數被調用纔會被then方法的回調函數接收*/){
resolve(value);
} else {
reject(error);
}
});
Promise
實例生成以後,可以用then
方法分別指定resolved
狀態和rejected
狀態的回調函數。
promise.then(function(value) {
// success 其中value爲異步操作成功時傳遞出來的參數
}, function(error) {
// failure 其中error爲異步操作失敗時傳遞出來的參數
});
所以來分析題目中的Promise實例,首先js中的邏輯運算符先了解一下,
a || b:如果a是true,那麼b不管是true還是false,都返回true。因此不用判斷b了,這個時候剛好判斷到a,因此返回a。
如果a是false,那麼就要判斷b,如果b是true,那麼返回true,如果b是false,返回false,其實不就是返回b了嗎。
a && b:如果a是false,那麼b不管是true還是false,都返回false,因此不用判斷b了,這個時候剛好判斷到a,因此返回a。
如果a是true,那麼就要在判斷b,和剛剛一樣,不管b是true是false,都返回b。
在javascript中:
以下內容會被當成false處理:"" , false , 0 , null , undefined , NaN
其他都是true
請看好同志們,i=9999這個是賦值操作,永遠都是true,所以少不了要成功調用resolve()方法,打印出4。
是的,這個題如果改爲i==9999,想必這個resolve()是調用不了了,4也就不能打印了,想象一下,如果題目就是這樣出的,你筆試的時候瞅錯了,看成i==999了,這是不是又是個讓你捶胸頓足的陷阱。
new Promise(function executor(resolve){
console.log(2);
for(var i=0;i<1000;i++){
i=9999 && resolve();
}
//console.log(3);
}).then(function(){
console.log(4);
})
繼續玩一下,你有沒有思考過,假如for循環1000次,這個then方法中執行成功的回調函數會執行幾次?打印什麼?
new Promise(function executor(resolve){
//console.log(2);
for(var i=0;i<1000;i++){
resolve(i);
}
}).then(function(data){
console.log(data);
})
//打印
//0
果然promise一旦狀態改變(成功調用resolve或者reject方法),就不會再變,所以打印0到999是不可能的少年!
(4)注意
創造Promise實例後,它會立即執行。
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
//打印順序
// Promise
// Hi!
// resolved
上述代碼中,Promise 新建後立即執行,所以首先輸出的是Promise
。然後,then
方法指定的回調函數,將在當前腳本所有同步任務執行完纔會執行,所以resolved
最後輸出。這也引出了,爲什麼5會在4之前打印。那麼3,4的順序是怎麼樣的呢?
這就是另外一個注意的考察點了。調用resolve
或reject
並不會終結 Promise 的參數函數的執行。
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
});
// 2
// 1
上面代碼中,調用resolve(1)
以後,後面的console.log(2)
還是會執行,並且會首先打印出來。這是因爲立即 resolved 的 Promise 是在本輪事件循環的末尾執行,總是晚於本輪循環的同步任務。所以,3在4之前打印。而5屬於同步任務,所以,綜合排序後,最後的結果是2,3,5,4,1。
(5)最後的最後玩一下
setTimeout(function(){
console.log(1);
},0)
new Promise(function executor(resolve){
console.log(2);
for(var i=0;i<1000;i++){
i=9999 && resolve();
}
console.log(3);
}).then(function(){
console.log(4);
})
//在來一個
new Promise(function executor(resolve){
console.log(22);
for(var i=0;i<1000;i++){
i=9999 && resolve();
}
console.log(33);
}).then(function(){
console.log(44);
})
console.log(5);
給你結果
2
3
22
33
5
4
44
1
只可意會不可言傳是也!
分析結束,你還滿意嗎?請多多指教!