ES6中關於Promise 對象的筆試題考法(前端面試重點考察的知識)

一道筆試題幫你梳理多個知識點。請寫出以下一段代碼輸出結果(用逗號隔開                                        

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構造函數接受一個函數作爲參數,該函數的兩個參數分別是resolverejectresolve函數在異步操作成功時調用,並將異步操作的結果,作爲參數傳遞出去;對應的,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的順序是怎麼樣的呢?

這就是另外一個注意的考察點了。調用resolvereject並不會終結 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

只可意會不可言傳是也!


分析結束,你還滿意嗎?請多多指教!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章