關於setTimeout和Promise執行順序問題

先看一段代碼html

console.log('打印'+1);
setTimeout(function(){
    console.log('打印'+2);
})
new Promise(function(resolve,reject){
        console.log('打印'+3);
      }).then(
  console.log('打印'+4));;
console.log('打印'+10);
new Promise(function(resolve,reject){
      setTimeout(function () {
        console.log('打印'+5);
      });
  }).then(
  console.log('打印'+6));
setTimeout(function(){
    new Promise(function(resolve,reject){
        console.log('打印'+7);
      });
})

執行結果:前端

console.log('打印'+1);
setTimeout(function(){
    console.log('打印'+2);
})
new Promise(function(resolve){
        console.log('打印'+3);
        resolve();
      }).then(function(){
        console.log(4);
      }
  );
console.log('打印'+10);
new Promise(function(resolve){
      setTimeout(function () {
        console.log('打印'+5);
      });
      resolve();
  }).then(function(){

  console.log('打印'+6)});
setTimeout(function(){
    new Promise(function(resolve){
        console.log('打印'+7);
      });
})
//執行結果:
//1;3;10;4;6;2;5;7

能夠看出Promise比setTimeout()先執行。vim

由於Promise定義以後便會當即執行,其後的.then()是異步裏面的微任務。promise

而setTimeout()是異步的宏任務。瀏覽器

 

引自https://www.cnblogs.com/woodyblog/p/6061671.html 網絡

js是單線程語言,但js的宿主環境(好比瀏覽器,Node)是多線程的,宿主環境經過某種方式(事件驅動,下文會講)使得js具有了異步的屬性。多線程

瀏覽器

js是單線程語言,瀏覽器只分配給js一個主線程,用來執行任務(函數),但一次只能執行一個任務,這些任務造成一個任務隊列排隊等候執行,但前端的某些任務是很是耗時的,好比網絡請求,定時器和事件監聽,若是讓他們和別的任務同樣,都老老實實的排隊等待執行的話,執行效率會很是的低,甚至致使頁面的假死。因此,瀏覽器爲這些耗時任務開闢了另外的線程,主要包括http請求線程,瀏覽器定時觸發器,瀏覽器事件觸發線程,這些任務是異步的。異步

任務隊列

剛纔說到瀏覽器爲網絡請求這樣的異步任務單獨開了一個線程,那麼問題來了,這些異步任務完成後,主線程怎麼知道呢?答案就是回調函數,整個程序是事件驅動的,每一個事件都會綁定相應的回調函數,舉個栗子,有段代碼設置了一個定時器函數

setTimeout(function(){
    console.log(time is out);
},1000);

執行這段代碼的時候,瀏覽器異步執行計時操做,當1000ms到了後,會觸發定時事件,這個時候,就會把回調函數放到任務隊列裏。整個程序就是經過這樣的一個個事件驅動起來的。
因此說,js是一直是單線程的,瀏覽器纔是實現異步的那個傢伙。oop

導圖要表達的內容用文字來表述的話:

  • 同步和異步任務分別進入不一樣的執行"場所",同步的進入主線程,異步的進入Event Table並註冊函數。
  • 當指定的事情完成時,Event Table會將這個函數移入Event Queue。
  • 主線程內的任務執行完畢爲空,會去Event Queue讀取對應的函數,進入主線程執行。
  • 上述過程會不斷重複,也就是常說的Event Loop(事件循環)。

 

主線程

js一直在作一個工做,就是從任務隊列裏提取任務,放到主線程裏執行。下面咱們來進行更深一步的理解。

 

(1)全部同步任務都在主線程上執行,造成一個執行棧(execution context stack)。

(2)主線程以外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。

(3)一旦"執行棧"中的全部同步任務執行完畢,系統就會讀取"任務隊列",看看裏面有哪些事件。那些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行。

(4)主線程不斷重複上面的第三步。

只要主線程空了,就會去讀取"任務隊列",這就是JavaScript的運行機制。這個過程會不斷重複。

Event Loop

主線程從"任務隊列"中讀取事件,這個過程是循環不斷的,因此整個的這種運行機制又稱爲Event Loop(事件循環)。

爲了更好地理解Event Loop,請看下圖(轉引自Philip Roberts的演講《Help, I'm stuck in an event-loop》)。

上圖中,主線程運行的時候,產生堆(heap)和棧(stack),棧中的代碼調用各類外部API,它們在"任務隊列"中加入各類事件(click,load,done)。只要棧中的代碼執行完畢,主線程就會去讀取"任務隊列",依次執行那些事件所對應的回調函數。

異步任務有宏任務和微任務。

2.宏任務macrotask:

(事件隊列中的每個事件都是一個macrotask)

優先級:主代碼塊 > setImmediate > MessageChannel > setTimeout / setInterval

好比:setImmediate指定的回調函數,老是排在setTimeout前面

3.微任務包括:

優先級:process.nextTick > Promise > MutationObserver

下面這個代碼輸出結果是什麼?

主程序和和settimeout都是宏任務,兩個promise是微任務

第一個宏任務(主程序)執行完,執行所有的微任務(兩個promise),再執行下一個宏任務(settimeout),因此結果爲:

執行結果:

相關文章
相關標籤/搜索