欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入理解JavaScript的事件執(zhí)行機(jī)制

 更新時間:2021年09月13日 09:27:59   作者:孟飯飯  
本文主要介紹了JavaScript的事件執(zhí)行機(jī)制,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下

前言

熟悉事件循環(huán),了解瀏覽器運(yùn)行機(jī)制將對我們理解 JavaScript 的執(zhí)行過程和排查運(yùn)行問題有很大幫助。以下是總結(jié)的一些瀏覽器事件循環(huán)的一些原理和示例。

瀏覽器 JS 異步執(zhí)行的原理

  • JS 是單線程的,也就是同一個時刻只能做一件事情,那么,為什么瀏覽器可以同時執(zhí)行異步任務(wù)呢?
  • 因為瀏覽器是多線程的,當(dāng) JS 需要執(zhí)行異步任務(wù)時,瀏覽器會另外啟動一個線程去執(zhí)行該任務(wù)。
  • 也就是說,JS 是單線程的指的是 執(zhí)行JS 代碼的線程只有一個,是瀏覽器提供的 JS 引擎線程(主線程)。瀏覽器中還有定時器線程和 HTTP 請求線程等,這些線程主要不是來跑 JS 代碼的。
  • 比如主線程中需要發(fā)一個 AJAX 請求,就把這個任務(wù)交給另一個瀏覽器線程(HTTP 請求線程)去真正發(fā)送請求,待請求回來了,再將 callback 里需要執(zhí)行的 JS 回調(diào)交給 JS 引擎線程去執(zhí)行。
  • 即瀏覽器才是真正執(zhí)行發(fā)送請求這個任務(wù)的角色,而 JS 只是負(fù)責(zé)執(zhí)行最后的回調(diào)處理。所以這里的異步不是 JS 自身實現(xiàn)的,其實是瀏覽器為其提供的能力。

瀏覽器中的事件循環(huán)

執(zhí)行棧與任務(wù)隊列

JS 在解析一段代碼時,會將同步代碼按順序排在某個地方,即執(zhí)行棧,然后依次執(zhí)行里面的函數(shù)。
當(dāng)遇到異步任務(wù)時就交給其他線程處理,待當(dāng)前執(zhí)行棧所有同步代碼執(zhí)行完成后,會從一個隊列中去取出已完成的異步任務(wù)的回調(diào)加入執(zhí)行棧繼續(xù)執(zhí)行。
遇到異步任務(wù)時又交給其他線程,.....,如此循環(huán)往復(fù)。
而其他異步任務(wù)完成后,將回調(diào)放入任務(wù)隊列中待執(zhí)行棧來取出執(zhí)行。

宏任務(wù)和微任務(wù)

根據(jù)任務(wù)的種類不同,可以分為微任務(wù)(micro task)隊列和宏任務(wù)(macro task)隊列。
事件循環(huán)的過程中,執(zhí)行棧在同步代碼執(zhí)行完成后,優(yōu)先檢查微任務(wù)隊列是否有任務(wù)需要執(zhí)行,如果沒有,再去宏任務(wù)隊列檢查是否有任務(wù)執(zhí)行,如此往復(fù)。
微任務(wù)一般在當(dāng)前循環(huán)就會優(yōu)先執(zhí)行,而宏任務(wù)會等到下一次循環(huán),因此,微任務(wù)一般比宏任務(wù)先執(zhí)行,并且微任務(wù)隊列只有一個,宏任務(wù)隊列可能有多個。另外我們常見的點(diǎn)擊和鍵盤等事件也屬于宏任務(wù)。

常見宏任務(wù):

  • setTimeout()
  • setInterval()
  • UI交互事件
  • postMessage
  • setImmediate() -- nodeJs

常見微任務(wù):

  • promise.then()、promise.catch()
  • new MutaionObserver()
  • process.nextTick() -- nodeJs

如下示例:

console.log('同步代碼1');
setTimeout(() => {
    console.log('setTimeout')
}, 0)

new Promise((resolve) => {
  console.log('同步代碼2')
  resolve()
}).then(() => {
    console.log('promise.then')
})
console.log('同步代碼3');

// 最終輸出"同步代碼1"、"同步代碼2"、"同步代碼3"、"promise.then"、"setTimeout"

具體分析如下:

  • setTimeout 回調(diào)和 promise.then 都是異步執(zhí)行的,將在所有同步代碼之后執(zhí)行;
  • 雖然 promise.then 寫在后面,但是執(zhí)行順序卻比 setTimeout 優(yōu)先,因為它是微任務(wù);
  • new Promise 是同步執(zhí)行的,promise.then 里面的回調(diào)才是異步的。

注意: 在瀏覽器中 setTimeout 的延時設(shè)置為 0 的話,會默認(rèn)為 4ms,NodeJS 為 1ms。
微任務(wù)和宏任務(wù)的本質(zhì)區(qū)別:

  • 宏任務(wù)特征:有明確的異步任務(wù)需要執(zhí)行和回調(diào);需要其他異步線程支持。
  • 微任務(wù)特征:沒有明確的異步任務(wù)需要執(zhí)行,只有回調(diào);不需要其他異步線程支持。

Async/await的運(yùn)行順序

特點(diǎn)

  • async聲明的函數(shù)只是把該函數(shù)的return包裝了,使得無論如何都會返回promise對象(非promise會轉(zhuǎn)化為promise{resolve})。
  • await聲明只能用在async函數(shù)中。
    • 當(dāng)執(zhí)行async函數(shù)時,遇到await聲明,會先將await后面的內(nèi)容按照'平常的執(zhí)行規(guī)則'執(zhí)行完(此處注意,當(dāng)執(zhí)行函數(shù)內(nèi)容中又遇到await聲明,此時接著執(zhí)行該await聲明內(nèi)容)。
    • 執(zhí)行完后立馬跳出async函數(shù),去執(zhí)行主線程其他內(nèi)容,等到主線程執(zhí)行完再回到await處繼續(xù)執(zhí)行后面的內(nèi)容。

示例

const a = async () => {
  console.log("a");
  await b();
  await e();
};

const b = async () => {
  console.log("b start");
  await c();
  console.log("b end");
};

const c = async () => {
  console.log("c start");
  await d();
  console.log("c end");
};

const d = async () => {
  console.log("d");
};

const e = async () => {
  console.log("e");
};

console.log('start');
a();
console.log('end');

運(yùn)行結(jié)果

個人分析

  • 在當(dāng)前同步環(huán)境中,先執(zhí)行console.log('start');輸出'start'。
  • 遇到同步函數(shù)a(),執(zhí)行a()。
  • a()是sync/await構(gòu)造,函數(shù)內(nèi)遇到console.log("a") 輸出 'a',遇到await聲明 await b(),為異步函數(shù),進(jìn)入函數(shù)b()內(nèi)執(zhí)行。(類似的操作,我自己想的)并將 await b()后的內(nèi)容推入微任務(wù)隊列中。我們可以記做[await b()后]。
  • b()是sync/await構(gòu)造,順序執(zhí)行遇到console.log("c start") 輸出 'c start',遇到await聲明 await c(),為異步函數(shù),進(jìn)入函數(shù)c()內(nèi)執(zhí)行。并將 await c()后的內(nèi)容推入微任務(wù)隊列中。我們可以記做[await c()后, await b()后]。
  • c()是sync/await構(gòu)造,順序執(zhí)行遇到console.log("b start") 輸出 'b start',遇到await聲明 await d(),為異步函數(shù),進(jìn)入函數(shù)d()內(nèi)執(zhí)行。并將 await d()后的內(nèi)容推入微任務(wù)隊列中。我們可以記做[await d()后, await c()后, await b()后]。
  • d()中,順序執(zhí)行遇到console.log("") 輸出 'd',d()函數(shù)運(yùn)行結(jié)束。
  • 這是執(zhí)行完 d()后,無可執(zhí)行異步函數(shù),此時進(jìn)入同步環(huán)境,執(zhí)行 a()后的內(nèi)容。
  • 遇到console.log("end") 輸出 'end'。此時同步環(huán)境中主線程執(zhí)行完,檢查微任務(wù)隊列是否有微任務(wù)。
  • 微任務(wù)隊列中[await d()后, await c()后, await b()后]有微任務(wù),執(zhí)行await d()后的內(nèi)容。
  • wait d()后的內(nèi)容是,console.log("c end"), 輸出 'c end'。此時內(nèi)容執(zhí)行完畢,再從微任務(wù)隊列中[await c()后, await b()后]檢查,執(zhí)行await c()后的內(nèi)容。
  • 執(zhí)行await c()后的內(nèi)容,console.log("b end");, 輸出 'b end'。此時內(nèi)容執(zhí)行完畢,再從微任務(wù)隊列中[await b()后]檢查,執(zhí)行await b()后的內(nèi)容。
  • await d()后的內(nèi)容是,await e(),遇到await聲明,執(zhí)行e()。并判斷并將 await e()后無運(yùn)行代碼,不用入微任務(wù)隊列。
  • 執(zhí)行e(),順序執(zhí)行console.log("e");,輸出 'e'。此時函數(shù)結(jié)束。
  • 微任務(wù)隊列中[]無微任務(wù),執(zhí)行結(jié)束。進(jìn)入同步環(huán)境。

到此這篇關(guān)于深入理解JavaScript的事件執(zhí)行機(jī)制的文章就介紹到這了,更多相關(guān)JavaScript 事件執(zhí)行機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

最新評論