JS中的同步異步編程,宏任務與微任務的執行順序


首先我們先看看同步與異步的定義,及瀏覽器的執行機制,方便我們更好地理解同步異步編程。
  
  瀏覽器是多線程的,JS是單線程的(瀏覽器只分配一個線程來執行JS)
 
  進程大線程小:一個進程中包含多個線程,例如在瀏覽器中打開一個HTML頁面就占用了一個進程,加載頁面的時候,瀏覽器分配一個線程去計算DOM樹,分配其它的線程去加載對應的資源文件...再分配一個線程去自上而下執行JS

  同步:在一個線程上(主棧/主任務隊列)同一個時間只能做一件事情,當前事情完成才能進行下一個事情(先把一個任務進棧執行,執行完成,在把下一個任務進棧,上一個任務出棧...)

  異步:在主棧中執行一個任務,但是發現這個任務是一個異步的操作,我們會把它移除主棧,放到等待任務隊列中(此時瀏覽器會分配其它線程監聽異步任務是否到達指定的執行時間),如果主棧執行完成,監聽者會把到達時間的異步任務重新放到主棧中執行...
    
  [宏任務:macro task]
        - 定時器
        - 事件綁定
        - ajax
        - 回調函數
        - Node中fs可以進行異步的I/O操作

 

  [微任務:micro task]
        - Promise(async/await)  => Promise並不是完全的同步,當在Excutor中執行resolve或者reject的時候,此時是異步操作,會先執行then/catch等,當主棧完成后,才會再去調用resolve/reject把存放的方法執行
        - process.nextTick (node中實現的api,把當前任務放到主棧最后執行,當主棧執行完,先執行nextTick,再到等待隊列中找)
   - MutationObserver   (創建並返回一個新的 MutationObserver 它會在指定的DOM發生變化時被調用。)
 

  執行順序優先級:SYNC => MICRO => MACRO

  所有JS中的異步編程僅僅是根據某些機制來管控任務的執行順序,不存在同時執行兩個任務這一說法

先來看一個例子:

setTimeout(() => {
    console.log(1);
}, 20);

setTimeout(() => {
    console.log(2);
}, 0);//=>默認會有最小的等待時間(V8一般是5~6MS)

console.time('WHILE');
let i = 0;
while (i <= 99999999) {
    i++;
}
console.timeEnd('WHILE');

setTimeout(() => {
    console.log(3);
}, 10);

console.log(4);

 結果輸出如圖:

我們先模擬下瀏覽器的程序執行過程,代碼自上而下執行,碰到第一個程序,先放入主棧(主任務隊列),此時瀏覽器發現這是一個宏任務定時器,把它移出主棧,放入等待任務隊列,再繼續執行下面的代碼,放入主棧執行,發現第二個任務也是宏任務的定時器,放入等待隊列,繼續往下執行,推入主棧,同步任務,循環99999999次之后輸出次數,再執行下一個程序,也移入等待隊列,再執行代碼,發現是同步任務,輸出4,此時主棧空閑,任務隊列到達時間后先進先出的原則,首先第二個任務到達時間,把它放入主棧執行,輸出2,此時本因輸出3,因為第三個程序是10ms到達,第一個是20s到達,但是第三個程序是等待247.849853515625ms后才放入的等待隊列,所以第一個程序先到達,輸出1,最后輸出3。

 

我們用ajax來看看js的同步與異步的執行順序和機制,AJAX任務開始:SEND,AJAX任務結束:狀態為4
let xhr = new XMLHttpRequest();
xhr.open('GET', 'xxx.txt', false);
// 放到等待區的時候,此時狀態是1
xhr.onreadystatechange = () => { 
    console.log(xhr.readyState);//=>4
};
xhr.send();
// 同步ajax,xhr.send時為同步,xhr.send()執行完后狀態為4,任務狀態為4的時候主棧空閑,onreadystatechange監聽到狀態變化,輸出4
 let xhr = new XMLHttpRequest();
 xhr.open('GET', 'xxx.txt', false);
 xhr.send();
// 狀態已經為4了
 xhr.onreadystatechange = () => {//=>狀態改變才會觸發,放到等待區的時候狀態已經為4了,不會在改變了,所以不會執行這個方法(啥都不會輸出)
     console.log(xhr.readyState);
 };
 let xhr = new XMLHttpRequest();
 xhr.open('GET', 'xxx.txt');
 xhr.send();//=>異步操作:執行SEND后,有一個線程是去請求數據,主棧會空閑下來
// 放等待區之前狀態是1
 xhr.onreadystatechange = () => {
     console.log(xhr.readyState);//=> 2 3 4
 };
// 主棧又空閑了
// 狀態為2  把函數執行
// 狀態為3  把函數執行
// 狀態為4  把函數執行


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM