JavaScript- Event Loop

一:什麼是單線程
你必定據說過一句話:JavaScript是單線程的語言。單線程就是每個當下,只有一件事情能被處理。相反地,做爲人類,咱們可以一邊寫代碼,一邊聽歌,這是由於咱們的大腦不是單線程的。假如把【寫代碼】和【聽歌】看作是咱們的JS代碼,對應咱們人類大腦的就是JS引擎,那麼在每個特定的時間點,JS引擎只能執行【寫代碼】或者【聽歌】,不能2者同時進行,這就是JS的單線程。函數

二:單線程怎樣保證不被阻塞 - Event Loop
想象一個場景:咱們經過發送一個HTTP請求去拿到用戶的名字,而後再用這個拿到的名字去render咱們的頁面。下面是一段僞代碼:oop

const name = HTTP.get('/user/{id}').reponse.name;
renderPage(name);

由於JS是單線程的,那就意味着咱們在執行第一行代碼(http請求)的時候,別的什麼事情都不能作,如今整個頁面都是卡住的,不能響應用戶的任何操做。這勢必是一個很是很差的體驗。那麼怎樣保證咱們的事件處理不被阻塞呢?答案就是Event Loop(事件輪詢)線程

什麼是Event Loop呢?能夠類比爲你天天上班,早上你一到公司你的領導就給你安排了10件不一樣的任務。雖然前面我說咱們人類是能夠一邊聽歌,一邊寫代碼的。可是通常工做上的事情,仍是得一件一件地作。因此,你把這10件任務寫進你今天的【to do list】。每作完一個任務,你就把它劃掉,而後立刻作下一個任務。code

三:JS Stack和Task Queue
咱們都知道代碼執行,會有一個入棧和出棧的過程,在JS的這個語境下,咱們暫且把這個棧叫作JS Stack。咱們前面說了,JS是單線程的,這就意味着咱們只有一個JS Stack。因此將要被執行的代碼,都得進入這個惟一的JS Stack,執行完了的代碼,則從JS Stack移除掉。server

咱們的一段JS代碼,包含着多個不一樣的task,就如一個to do list,包含着多個不一樣的任務同樣。咱們的任務有輕重緩急之分,有着不一樣的優先級。咱們的JS task也同樣,有着不一樣的類別,通常來講JS Task能夠分爲如下2類:事件

1: MacroTask: script(總體代碼), setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI Rendering
2: MicroTask: Promise, queueMicrotask, process.nextTick, Object.observe, MutationObserver

當咱們的JS引擎去遇到多個task的時候,就會把這些task進行排隊,從而就造成了task queue。前面咱們說了task能夠分爲2類,天然地就會有2個類型的task queue:MacroTask queueMicroTask queueip

JS的同步代碼會按照代碼的出現順序,放入JS Stack被執行。只有當前的JS Stack空了的時候,MacroTask queue或者MicroTask queue被推入JS Stack,繼而被執行get

咱們立刻來看一下例子,加深對上面這段加粗的文字的理解:回調函數

setTimeout(function f1(){console.log('2')}, 0);  
console.log('1');

上面代碼的輸出結果是1,2,而不是2,1。要理解這個問題,咱們先來認識一下setTimeout()和Web API的概念。同步

四:ECMAScript和Web API
咱們常常在咱們的JS代碼裏面使用setTimeout(),可能天然地把setTimeout算做了ECMAScript的一部分。可是,其實並非這樣的。咱們經常使用的setTimeout(),setInterval()等實際上是屬於Web API的範疇。舉個例子:

setTimeout(function f1(){}, 500);

以上代碼的執行過程能夠被理解爲:Web API拿到setTimeout()的2個參數:回調函數f1()和延時500毫秒。Web API開始一個500毫秒的記時,在500毫秒到來以前,f1()會被Web API一直拿着,一旦500毫秒時間到,這個f1()就被推入MacroTask queue。

理解了Web API的工做過程以後,咱們再來回顧以前的例子:

setTimeout(function f1(){console.log('2')}, 0);  
console.log('1');

如今來分析一下上面這段代碼的執行過程:
step1: setTimeout()方法進入JS Stack被執行,可是其實是調用Web API去執行。咱們的延時是0秒,就意味着Web API會當即把回調函數推入MacroTask queue。再次強調:進入task queue並不會被執行,要進入JS Stack纔會被執行。

step2: 由於setTimeout()執行完了(注意是setTimeout()這個函數自己被執行了,而不是它的callback函數被執行了。),當前JS Stack爲空,又由於console.log('1');是同步代碼,因此會被當即放入JS Stack且執行,因而獲得打印的值:1。

step3: 上一步執行完以後,當前JS Stack爲空,因此從MacroTask queue裏面取出f1()函數放入JS Stack執行,獲得打印值: 2。

以上就是打印值1,2的由來。如今你明白了,爲何明明setTimeout()在前面,可是打印值卻在後面。

相關文章
相關標籤/搜索