摘要:創(chuàng)建全局上下文由表示,并將全局上下文推到棧頂。在了解異步執(zhí)行之前還需要知道一些概念,事件循環(huán)和回調(diào)隊(duì)列也稱為任務(wù)隊(duì)列或消息隊(duì)列。會(huì)等待事件循環(huán)調(diào)度。事件循環(huán)事件循環(huán)的作用是查看調(diào)用棧并確定調(diào)用棧是否空閑。
簡評(píng):如果你對(duì) JavaScript 異步的原理感興趣,這里有一篇不錯(cuò)的介紹。JavaScript 同步代碼是如果工作的
在介紹 JavaScript 異步執(zhí)行之前先來了解一下, JavaScript 同步代碼是如何執(zhí)行的。
這里有兩個(gè)概念需要了解:
執(zhí)行上下文(Excution Context)
執(zhí)行上下文是一個(gè)抽象的概念,用于表示 JavaScript 的運(yùn)行環(huán)境,任何代碼都會(huì)有一個(gè)執(zhí)行上下文。
全局代碼運(yùn)行在全局執(zhí)行上下文,函數(shù)里的代碼運(yùn)行在函數(shù)執(zhí)行上下文,每一個(gè)函數(shù)都有自己的執(zhí)行上下文。
調(diào)用堆棧(Call Stack)
調(diào)用棧是一個(gè)具有 LIFO(后進(jìn)先出)結(jié)構(gòu)的棧,用于儲(chǔ)存代碼執(zhí)行階段所有的執(zhí)行上下文。
因?yàn)?JavaScript 是單線程的,所以 JavaScript 只有一個(gè)多帶帶的調(diào)用棧。
我們以下面例子介紹同步代碼執(zhí)行過程。
const second = () => { console.log("Hello there!"); } const first = () => { console.log("Hi there!"); second(); console.log("The End"); } first();
創(chuàng)建全局上下文(由 main() 表示),并將全局上下文推到棧頂。然后依次將遇到函數(shù)執(zhí)行上下文推到棧頂(如果函數(shù)中執(zhí)行其他他函數(shù),其他函數(shù)依次推到棧頂以此類推)。當(dāng)函數(shù)執(zhí)行完畢對(duì)應(yīng)的執(zhí)行上下文會(huì)從調(diào)用棧彈出,程序結(jié)束時(shí)全局上下文從調(diào)用棧彈出。
JavaScript 異步代碼是如何執(zhí)行的?通過上個(gè)章節(jié)我們已經(jīng)對(duì)調(diào)用棧和 JavaScript 的同步執(zhí)行有了基本的了解,現(xiàn)在來看看 JavaScript 異步執(zhí)行是如何工作的。
什么是阻塞?
由于 JavaScript 是單線程的,如果某個(gè)函數(shù)耗費(fèi)的時(shí)間比較長,會(huì)阻塞后面的任務(wù)執(zhí)行,這就造成了阻塞。解決阻塞最簡單的方法是函數(shù)直接返回不等待,使用異步回調(diào)來處理返回結(jié)果。
在了解 JavaScript 異步執(zhí)行之前還需要知道一些概念,事件循環(huán)和回調(diào)隊(duì)列(也稱為任務(wù)隊(duì)列或消息隊(duì)列)。
注意:Event Loop 、Web APIs 和 Message Queue 并不是 JavaScript 引擎的一部分,而是瀏覽器運(yùn)行時(shí)環(huán)境和 Nodejs 運(yùn)行時(shí)環(huán)境的一部分。
我們以下面代碼為例,解釋異步代碼是如何執(zhí)行的。
const networkRequest =()=> { setTimeout(()=> { console.log("Async Code"); },2000); }; console.log("Hello World"); networkRequest(); console.log("The End");
當(dāng)上述程序加載到瀏覽器時(shí) console.log(‘Hello World’) 代碼執(zhí)行時(shí)會(huì)一次在調(diào)用棧推入和彈出。遇到 networkRequest() 將其推入到調(diào)用棧頂。然后繼續(xù)將 networkRequest 內(nèi)的 setTimeout 方法推入棧頂,隨后 setTimeout networkRequest 依次出棧。最后對(duì) console.log(‘The End’) 進(jìn)行入棧出棧。
當(dāng) timer 到期后會(huì)將 callback 推入 message queue(消息隊(duì)列)中,此時(shí) callback 不會(huì)馬上執(zhí)行。會(huì)等待事件循環(huán)調(diào)度。
事件循環(huán)
事件循環(huán)的作用是查看調(diào)用棧并確定調(diào)用棧是否空閑。如果調(diào)用??臻e,even loop 會(huì)查看消息隊(duì)列是否有待處理的 callback 需要觸發(fā)。例子中的消息隊(duì)列只包含一個(gè) callback,當(dāng)調(diào)用棧為空的時(shí)候,even loop 會(huì)將 callback 推入調(diào)用棧中觸發(fā) networkRequest 的回調(diào)。
DOM 事件
消息隊(duì)列還會(huì)包含來自 DOM 的事件回調(diào),比如鼠標(biāo)和鍵盤事件回調(diào)。例如:
document.querySelector(".btn").addEventListener("click",function callback(event) { console.log("Button Clicked"); });
對(duì)于 DOM 事件,當(dāng)具體的事件觸發(fā)會(huì)將 callback 推入消息隊(duì)列中,等待 even loop 來調(diào)度執(zhí)行。
ES6 job queue/micro-task queue
ES6 新增了 job queue/micro-task queue 概念,在 Promise 中用到。job queue 比 message queue 擁有更高的優(yōu)先級(jí)。意味著 job queue 和 message queue 都有任務(wù)時(shí)會(huì)優(yōu)先執(zhí)行 job queue 中的任務(wù)。例如:
console.log("Script start"); // callback 在 message queue 中 setTimeout(function callback() { console.log("setTimeout"); }, 0); // 任務(wù)在 micro-task queue 中 new Promise((resolve, reject) => { resolve("Promise resolved"); }).then(res => console.log(res)) .catch(err => console.log(err)); console.log("Script End"); // 輸出: Script start Script End Promise resolved setTimeout
再來看下一個(gè)例子(兩個(gè) setTimeout 和 兩個(gè) Promise):
console.log("Script start"); setTimeout(() => { console.log("setTimeout 1"); }, 0); setTimeout(() => { console.log("setTimeout 2"); }, 0); new Promise((resolve, reject) => { resolve("Promise 1 resolved"); }).then(res => console.log(res)) .catch(err => console.log(err)); new Promise((resolve, reject) => { resolve("Promise 2 resolved"); }).then(res => console.log(res)) .catch(err => console.log(err)); console.log("Script End"); //輸出為 Script start Script End Promise 1 resolved Promise 2 resolved setTimeout 1 setTimeout 2
由此可見 micro-task queue 中的所有任務(wù)都會(huì)優(yōu)先于 message queue 中的任務(wù)執(zhí)行。
原文:Understanding Asynchronous JavaScript?—?the Event Loop
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/99753.html
摘要:而事件循環(huán)是主線程中執(zhí)行棧里的代碼執(zhí)行完畢之后,才開始執(zhí)行的。由此產(chǎn)生的異步事件執(zhí)行會(huì)作為任務(wù)隊(duì)列掛在當(dāng)前循環(huán)的末尾執(zhí)行。在下,觀察者基于監(jiān)聽事件的完成情況在下基于多線程創(chuàng)建。 主要問題: 1、JS引擎是單線程,如何完成事件循環(huán)的? 2、定時(shí)器函數(shù)為什么計(jì)時(shí)不準(zhǔn)確? 3、回調(diào)與異步,有什么聯(lián)系和不同? 4、ES6的事件循環(huán)有什么變化?Node中呢? 5、異步控制有什么難點(diǎn)?有什么解決方...
摘要:概述本篇主要介紹的運(yùn)行機(jī)制單線程事件循環(huán)結(jié)論先在中利用運(yùn)行至完成和非阻塞完成單線程下異步任務(wù)的處理就是先處理主模塊主線程上的同步任務(wù)再處理異步任務(wù)異步任務(wù)使用事件循環(huán)機(jī)制完成調(diào)度涉及的內(nèi)容有單線程事件循環(huán)同步執(zhí)行異步執(zhí)行定時(shí)器的事件循環(huán)開始 1.概述 本篇主要介紹JavaScript的運(yùn)行機(jī)制:單線程事件循環(huán)(Event Loop). 結(jié)論先: 在JavaScript中, 利用運(yùn)行至...
摘要:從異步過程的角度看,函數(shù)就是異步過程的發(fā)起函數(shù),事件監(jiān)聽函數(shù)就是異步過程的回調(diào)函數(shù)。事件觸發(fā)時(shí),表示異步任務(wù)完成,會(huì)將事件監(jiān)聽器函數(shù)封裝成一條消息放到消息隊(duì)列中,等待主線程執(zhí)行。 1.為什么JavaScript是單線程? JavaScript語言的一大特點(diǎn)就是單線程,也就是說,同一個(gè)時(shí)間只能做一件事。那么,為什么JavaScript不能有多個(gè)線程呢?這樣能提高效率啊。JavaScrip...
摘要:事件循環(huán)事件循環(huán)是指主線程重復(fù)從消息隊(duì)列中取消息執(zhí)行的過程。事件觸發(fā)時(shí),表示異步任務(wù)完成,會(huì)將事件監(jiān)聽器函數(shù)封裝成一條消息放到消息隊(duì)列中,等待主線程執(zhí)行。 一. 單線程 我們常說JavaScript是單線程的。 所謂單線程,是指在JS引擎中負(fù)責(zé)解釋和執(zhí)行JavaScript代碼的線程只有一個(gè)。不妨叫它主線程。 但是實(shí)際上還存在其他的線程。例如:處理AJAX請(qǐng)求的線程、處理DOM事件的線...
摘要:主線程不斷重復(fù)上面的三步,此過程也就是常說的事件循環(huán)。所以主線程代碼執(zhí)行時(shí)間過長,會(huì)阻塞事件循環(huán)的執(zhí)行。參考資料這一次,徹底弄懂執(zhí)行機(jī)制任務(wù)隊(duì)列的順序機(jī)制事件循環(huán)搞懂異步事件輪詢與中的事件循環(huán) 1. 說明 讀過本文章后,您能知道: JavaScript代碼在瀏覽器中的執(zhí)行機(jī)制和事件循環(huán) 面試中經(jīng)常遇到的代碼輸出順序問題 首先通過一段代碼來驗(yàn)證你是否了解代碼輸出順序,如果你不知道輸出...
摘要:異步請(qǐng)求線程在在連接后是通過瀏覽器新開一個(gè)線程請(qǐng)求將檢測到狀態(tài)變更時(shí),如果設(shè)置有回調(diào)函數(shù),異步線程就產(chǎn)生狀態(tài)變更事件,將這個(gè)回調(diào)再放入事件循環(huán)隊(duì)列中。 基礎(chǔ):瀏覽器 -- 多進(jìn)程,每個(gè)tab頁獨(dú)立一個(gè)瀏覽器渲染進(jìn)程(瀏覽器內(nèi)核) 每個(gè)瀏覽器渲染進(jìn)程是多線程的,主要包括:GUI渲染線程 JS引擎線程 也稱為JS內(nèi)核,負(fù)責(zé)處理Javascript腳本程序。(例如V8引擎) JS引擎線程負(fù)...
閱讀 759·2021-11-24 10:19
閱讀 1148·2021-09-13 10:23
閱讀 3464·2021-09-06 15:15
閱讀 1802·2019-08-30 14:09
閱讀 1726·2019-08-30 11:15
閱讀 1874·2019-08-29 18:44
閱讀 967·2019-08-29 16:34
閱讀 2486·2019-08-29 12:46