最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解JS事件循環(huán)及宏任務微任務的原理

 更新時間:2022年05月07日 08:51:28   作者:諸葛小愚  
在js中,我們一般將所有的任務都分成兩類,一種是同步任務,另外一種是異步任務。而在異步任務中,又有著更加細致的分類,那就是微任務和宏任務。本文將詳細講解這二者的原理與使用,需要的可以參考一下

本質上來說,JavaScript是同步的、阻塞的、單線程語言,不管是在瀏覽器中還是nodejs環(huán)境下。瀏覽器在執(zhí)行js代碼和渲染DOM節(jié)點都是在同一個線程中,執(zhí)行js代碼就無法渲染DOM,渲染DOM的時候就無法執(zhí)行js代碼。如果按照這種同步方式執(zhí)行,頁面的渲染將會出現白屏甚至是報錯,特別是遇到一些耗時比較長的網絡請求或者js代碼,因此在實際開發(fā)中一般是通過異步的方式解決。

什么是異步?js是一步一步執(zhí)行代碼的,遇到alert這種阻塞代碼時,js將會停止往下執(zhí)行直到阻塞代碼執(zhí)行完畢。異步就是將函數放在單獨的異步隊列中,不會產生阻塞,js可以繼續(xù)往下執(zhí)行,等到同步代碼執(zhí)行完畢后再執(zhí)行異步隊列中的函數。因此,js會先執(zhí)行完同步代碼,才會執(zhí)行異步代碼。

異步也有細分,每一個異步函數可當作是一個待執(zhí)行的任務,可分為宏任務和微任務。本文重點介紹瀏覽器的事件循環(huán),nodejs環(huán)境下的事件循環(huán)放在下一篇中繼續(xù)探討。

宏任務

宏任務,也可簡單的說成是任務,在下一輪DOM渲染之后執(zhí)行。常見的宏任務有:

setTimeout:設置一個定時器,該定時器會在設置的延遲時間到期后執(zhí)行一個函數或者指定的代碼塊。值得注意的是,setTimeout不一定會在延遲時間到達后就立即執(zhí)行函數,而是會判斷執(zhí)行隊列中是否還有函數沒有處理,如果沒有了并且棧為空,setTimeout才會在延遲時間到達后執(zhí)行函數。

// setTimeout 延遲執(zhí)行不等于到期時立即執(zhí)行
let now = new Date().getSeconds();
setTimeout(() => {
    console.log('this is setTimeout 0');
}, 0);
setTimeout(() => {
    console.log('this is setTimeout 200');
}, 200);
while(true) {
    if (new Date().getSeconds() - now >= 2) {
        console.log('break out while loop');
        break;
    }
}

運行結果

break out while loop
this is setTimeout 0
this is setTimeout 200

先執(zhí)行同步代碼,再執(zhí)行異步。setTimeout(() => {}, 0)表示0毫秒后立即執(zhí)行函數,但是當前執(zhí)行隊列中還有未處理完的while循環(huán),因此需要等到while循環(huán)執(zhí)行完畢后,才會根據延遲到期時間執(zhí)行函數。

setInterval:設置定時器,表示在固定的時間間隔內,重復執(zhí)行某一函數或者特定的代碼塊。注意使用setInterval有最小延遲時間限制以及確保執(zhí)行時間要小于間隔時間,如果執(zhí)行時間無法確定,則應采用遞歸調用setTimeout的方式代替。

網絡請求:只要是指XMLHttpRequest等網絡請求

微任務

微任務,在下一輪DOM渲染之前執(zhí)行,微任務比宏任務更早執(zhí)。常見的微任務有:

promise:表示一個異步操作最終的結果和返回值,可能會失敗,也可能成功。異步函數在執(zhí)行時,什么時候返回結果是不可預料的,Promise把異步操作的返回值和函數關聯起來,保證在異步執(zhí)行結束后會執(zhí)行對應的函數,并通過函數返回操作值。這種效果就類似于把異步代碼“同步執(zhí)行”。

queueMicrotask:將函數添加到微任務隊

console.log('start');
// 微任務隊列
Promise.resolve().then(() => {
    console.log('promise then');
});
queueMicrotask(() => {
    console.log('queueMicrotask');
});
console.log('end');

運行結果

start
end
promise then
queueMicrotask

事件循環(huán)

因為有異步操作的存在,所以出現了事件循環(huán),如果都是同步操作,一行一行執(zhí)行代碼,事件循環(huán)也就失去了用武之地。在了解事件循環(huán)前,還需要補充js的執(zhí)行過程:

js在執(zhí)行代碼時,遇到函數就會將其添加到調用棧中,每一幀都會存儲當前函數的參數和局部變量,當一個函數執(zhí)行完畢,則會從調用棧中彈出,直到棧被清空,那么程序也就執(zhí)行完畢。在執(zhí)行的過程中,需要的引用數據都是從堆中獲取。

在實際開發(fā)中,往往是同步代碼和異步代碼都有。在js執(zhí)行時,還是從第一行代碼開始執(zhí)行,遇到函數就將其添加到棧中,然后執(zhí)行同步操作;如果遇到異步函數,則根據其類型,宏任務就添加到宏任務隊列,微任務添加到微任務隊列。直到同步代碼執(zhí)行完畢,則開始執(zhí)行異步操作。

異步操作后于同步操作,異步操作內部也是分先后順序的??偟膩碚f:

  • 微任務先于宏任務執(zhí)行
  • 微任務與微任務之間根據先后順序執(zhí)行,宏任務與宏任務之間根據延遲時間順序執(zhí)行
  • 微任務在下一輪DOM渲染前執(zhí)行,宏任務在下一輪DOM渲染之后執(zhí)行
  • 每個任務的執(zhí)行都是一次出棧操作,直到棧被清空

微任務比宏任務先執(zhí)行

console.log('start');
// 宏任務隊列
setTimeout(() => {
    console.log('setTimeout');
});
// 微任務隊列
Promise.resolve().then(() => {
    console.log('promise then');
});
console.log('end');

執(zhí)行結果

start
end
promise then
setTimeout

微任務在下一輪DOM渲染前執(zhí)行,宏任務在之后執(zhí)行

let div = document.createElement('div');
div.innerHTML = 'hello world';
document.body.appendChild(div);
let divList = document.getElementByTagName('div');
console.log('同步任務 length ---', list.length);
console.log('start');
setTimeout(() => {
    console.log('setTimeout length ---', list.length);
    alert('宏任務 setTimeout 阻塞'); // 使用alert阻塞js執(zhí)行
});
Promise.resolve().then(() => {
    console.log('promise then length ---', list.length);
    alert('微任務 promise then 阻塞);
});
console.log('end');

事件循環(huán)

event loop會持續(xù)監(jiān)聽是否有異步操作,如果有則添加到對應的隊列中,等待執(zhí)行。例如在宏任務中添加微任務,或者在微任務中添加宏任務,當前任務執(zhí)行完后,可能還會有新的任務添加到事件循環(huán)中。

宏任務與微任務

微任務中創(chuàng)建宏任務

    new Promise((resolve) => {
      console.log('promise 1');
      setTimeout(() => {
        console.log('setTimeout 1');
      }, 500);
      resolve();
    }).then(() => {
      console.log('promise then');
      setTimeout(() => {
        console.log('setTimeout 2');
      }, 0);
    });
    new Promise((resolve) => {
      console.log('promise 2');
      resolve();
    })

運行結果

promise 1
promise 2
promise then
setTimeout 2
setTimeout 1

解析

js執(zhí)行代碼,遇到兩個Promise,則分別添加到微任務隊列,同步代碼執(zhí)行完畢。

在微任務隊列中根據先進先出,第一個Promise先執(zhí)行,遇到setTimeout,則添加到宏任務隊列,resolve()返回執(zhí)行結果并執(zhí)行then,事件循環(huán)將其繼續(xù)添加到微任務隊列;第一個Promise執(zhí)行完畢,執(zhí)行第二個Promise。

繼續(xù)執(zhí)行微任務隊列,直到清空隊列。遇到setTimeout,并將其添加到宏任務隊列

宏任務隊列現在有兩個任務待執(zhí)行,由于第二個setTimeout的延遲事件更小,則優(yōu)先執(zhí)行第二個;如果相等,則按照順序執(zhí)行。

繼續(xù)執(zhí)行宏任務隊列,直到清空隊列。

宏任務中創(chuàng)建微任務

    setTimeout(() => {
      console.log('setTimeout 1');
      new Promise((resolve) => {
        console.log('promise 1');
        resolve();
      }).then(() => {
        console.log('promise then');
      })
    }, 500);
    setTimeout(() => {
      console.log('setTimeout 2');
      new Promise((resolve) => {
        console.log('promise 2');
        resolve();
      })
    }, 0);

運行結果

setTimeout 2
promise 2
setTimeout 1
promise 1
promise then

解析

js執(zhí)行代碼,遇到兩個setTimeout,將其添加到宏任務隊列,同步代碼執(zhí)行完畢

先檢查微任務隊列中是否有待處理的,剛開始肯定沒有,因此直接執(zhí)行宏任務隊列中的任務。第二個為零延遲,需要優(yōu)先執(zhí)行。遇到Promise,將其添加到微任務隊列。第一個宏任務執(zhí)行完畢

在執(zhí)行第二個宏任務時,微任務隊列中已經存在待處理的,因此需要先執(zhí)行微任務。

微任務執(zhí)行完畢,并且延遲時間到期,第一個setTimeout開始執(zhí)行。遇到Promise,將其添加到微任務隊列中

執(zhí)行微任務隊列中的Promise,執(zhí)行完畢后遇到then,則將其繼續(xù)添加到微任務隊列

直到所有微任務執(zhí)行完畢

宏任務中創(chuàng)建宏任務

    setTimeout(() => {
      console.log('setTimeout 1');
      setTimeout(() => {
        console.log('setTimeout 2');
      }, 500);
      setTimeout(() => {
        console.log('setTimeout 3');
      }, 500);
      setTimeout(() => {
        console.log('setTimeout 4');
      }, 100);
    }, 0);

運行結果

setTimeout 1
setTimeout 4
setTimeout 2
setTimeout 3

解析

宏任務中創(chuàng)建宏任務,執(zhí)行順序一般來說是按照先后順序的。對于setTImeout來說,延遲時間相同,則按照先后順序執(zhí)行;延遲時間不同,則按照延遲時間的大小先后順序執(zhí)行

微任務中創(chuàng)建微任務

    new Promise((resolve) => {
      console.log('promise 1');
      new Promise((resolve) => {
        console.log('promise 2');
        resolve();
      });
      new Promise((resolve) => {
        console.log('promise 3');
        resolve();
      })
      resolve();
    })

運行結果

promise 1
promise 2
promise 3

解析

微任務中創(chuàng)建微任務,執(zhí)行順序一般來說是按照先后順序執(zhí)行的。

總結

  • 同步代碼直接執(zhí)行,異步代碼添加到宏任務隊列或者微任務隊列
  • 微任務在下一輪DOM渲染前執(zhí)行,宏任務在下一輪DOM渲染之后執(zhí)行
  • 事件循環(huán)持續(xù)監(jiān)聽
  • 如果存在異步操作,需要將關聯代碼放在異步函數中執(zhí)行
  • 如果代碼層次比較復雜,同步、異步代碼混雜,一定要理清代碼的執(zhí)行順序。避免因為異步,導致代碼出現難以察覺的bug

到此這篇關于詳解JS事件循環(huán)及宏任務微任務的原理的文章就介紹到這了,更多相關JS宏任務 微任務內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • js multiple全選與取消全選實現代碼

    js multiple全選與取消全選實現代碼

    本文章總結了利用jquery與js實現multiple全選與取消全選代碼有需要參考的朋友可參考下
    2012-12-12
  • ES6的一些實用高級技巧總結

    ES6的一些實用高級技巧總結

    ES6不僅在語法上有很大的改進,在代碼的組織結構上也有重大升級,下面這篇文章主要給大家介紹了關于ES6的一些實用高級技巧的相關資料,需要的朋友可以參考下
    2023-12-12
  • Node.js開發(fā)指南中的簡單實例(mysql版)

    Node.js開發(fā)指南中的簡單實例(mysql版)

    工作原因需要用到nodejs,于是找到了《node.js開發(fā)指南》這本書來看看,作者BYVoid 為清華大學計算機系的高材生,年紀竟比我還小一兩歲,中華地廣物博真是人才輩出,佩服
    2013-09-09
  • Javascript中的return作用及javascript return關鍵字用法詳解

    Javascript中的return作用及javascript return關鍵字用法詳解

    本文給大家介紹Javascript中的return作用及javascript return關鍵字用法詳解,對于大家學習javascript中的return知識有所幫助,感興趣的朋友一起學習吧
    2015-11-11
  • js中[]、{}、()區(qū)別示例淺析

    js中[]、{}、()區(qū)別示例淺析

    很多人都知道在js中[]表示的是數組對象,{}表示的是對象,但是這兩者的區(qū)別卻說不出來,下面這篇文章主要給大家介紹了關于js中[]、{}、()區(qū)別的相關資料,需要的朋友可以參考下
    2023-06-06
  • 微信小程序基于canvas漸變實現的彩虹效果示例

    微信小程序基于canvas漸變實現的彩虹效果示例

    這篇文章主要介紹了微信小程序基于canvas漸變實現的彩虹效果,結合實例形式分析了微信小程序線性漸變及圓形漸變的相關實現技巧,需要的朋友可以參考下
    2019-05-05
  • JavaScript用20行代碼實現虎年春節(jié)倒計時

    JavaScript用20行代碼實現虎年春節(jié)倒計時

    春節(jié)將至,相信大家跟小編一樣很激動呀。為了迎接虎年春節(jié)到來,小編為大家準備了一個虎年春節(jié)倒計時,僅20行代碼用js就實現啦。感興趣的可以動手試一試
    2022-01-01
  • 詳解webpack自動生成html頁面

    詳解webpack自動生成html頁面

    本篇文章主要介紹了詳解webpack自動生成頁面,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • js簡單實現點擊左右運動的方法

    js簡單實現點擊左右運動的方法

    這篇文章主要介紹了js簡單實現點擊左右運動的方法,實例分析了javascript實現左右運動的相關要點與技巧,非常具有實用價值,需要的朋友可以參考下
    2015-04-04
  • javascript 樹形導航菜單實例代碼

    javascript 樹形導航菜單實例代碼

    本文章來給大家提供一款適用于網站后臺的使用的javascript 樹形導航菜單特效代碼,有需要了解的同學可以參考一下
    2013-08-08

最新評論

阳城县| 东丽区| 白水县| 梅州市| 万安县| 原平市| 颍上县| 丰顺县| 东安县| 毕节市| 贡嘎县| 大埔区| 丰宁| 平武县| 城口县| 赫章县| 当阳市| 新化县| 尚志市| 顺昌县| 凤台县| 闽侯县| 黄山市| 武清区| 双流县| 莱西市| 闸北区| 酒泉市| 西宁市| 西华县| 南陵县| 正宁县| 吉水县| 酒泉市| 华安县| 朝阳区| 廉江市| 嘉义县| 隆安县| 如东县| 桐梓县|