歡迎關注 jsliang 的文件庫 —— 一個窮盡一生更新的倉庫,檢視更多技術、理財、健身文章:github.com/LiangJunron…
一 目錄
不折騰的前端,和鹹魚有什麼區別
目錄 |
---|
一 目錄 |
二 前言 |
三 Event Loop |
四 瀏覽器 Event Loop |
4.1 示例 1 |
4.2 示例 2 |
4.3 示例 3 |
4.4 小結 |
五 Node.js Event Loop |
5.1 setTimeout & setImmediate |
5.2 process.nextTick() |
5.3 示例 1 |
5.4 示例 2 |
5.5 小結 |
六 總結 |
七 參考文獻 |
二 前言
Hello 小夥伴們早上好、中午好、下午好、晚上好、凌晨好~
在日常工作中,你有沒有碰到過這種疑惑:
- 疑惑一:為什麼這份程式碼它不按照我的意思走?為啥不是輸出
1 2 3
?
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 1000);
}
// console:
// 3
// 3
// 3
複製程式碼
- 疑惑二:為什麼這份程式碼它也不按照我的意思走?為啥不是輸出
jsliang
?
let name;
setTimeout(() => {
name = '樑峻榮';
console.log(name);
}, 1000);
if (name) {
name = 'jsliang';
console.log(name);
}
// console: '樑峻榮'
複製程式碼
孩子沒娘,說來話長。
既然說來話長,jsliang 只能嘗試長話短說了:
- 本篇文章從 Event Loop 起因說起,通過探討 瀏覽器環境 Event Loop 和 Node.js 環境 Event Loop,從而解惑工作中產生的困擾,擴充套件你面試知識點。
這麼一說,我們也好對文章進行劃分了:
- 第三章 Event Loop:解釋 Event Loop 產生原因和程式碼演示。
- 第四章 瀏覽器 Event Loop:解惑工作困擾和擴充套件必備面試知識點。
- 第五章 Node.js Event Loop:進一步探索瀏覽器和 Node.js 中 Event Loop 的不同。
OK,Let's go!
三 Event Loop
- 問:什麼是 Event Loop,為什麼需要 Event Loop?
答:
首先,我們需要知道的是:JavaScript 是單執行緒的。
單執行緒意味著,所有任務都需要排隊,前一個任務結束,才會執行後一個任務。
假設 jsliang 和 JavaScript 一樣一次只能做一件事,那麼大概就是如下圖所示。
而這種 主執行緒從 “任務佇列” 中讀取執行事件,不斷迴圈重複的過程,就被稱為 事件迴圈(Event Loop)。
然後,如果前一個任務耗時很長,後一個任務就不得不一直等著,那麼我們肯定要對這種情況做一些特殊處理,畢竟很多時候我們並不是完全希望它如此執行。
所以為了協調事件(event),使用者互動(user interaction),指令碼(script),渲染(rendering),網路(networking)等,使用者代理(user agent)必須使用事件迴圈(event loops)。
這樣,在瞭解 瀏覽器 Event Loop 和 Node.js Event Loop 的情況下,我們就可以瞭解它的執行過程。
通過自身的瞭解,來處理一些較為棘手的問題。
為了加深小夥伴們的印象,可以看下圖:
jsliang 日常中,強制被加上了 “被豆豆媽打”(廢話,豆豆那麼可愛,你怎麼可以打豆豆)。
當然,這個被打的順序也不一定是在後面,可能打多兩次後,“睡覺” 完之後就是 “被豆豆媽打” 了。
通過這個解釋,小夥伴們應該知道為啥有 瀏覽器 Event Loop 和 Node.js Event Loop 了。
等等,你剛才說到了 瀏覽器 Event Loop 和 Node.js Event Loop,為什麼都是關於 JavaScript 的,在這兩部分都不一樣呢?
- 簡單來說:你的頁面放到了瀏覽器去展示,你的資料放到了後臺處理(將 Node.js 看成 PHP、Java 等後端語言),這兩者能沒有區別麼?!
你說了跟沒說一樣,為什麼會這樣你沒有解釋啊!
好的,說得再仔細點:
- Node.js:Node.js 的 Event Loop 是基於 libuv。libuv 已經對 Event Loop 作出了實現。
- 瀏覽器:瀏覽器的 Event Loop 是基於 HTML5 規範的。而 HTML5 規範中只是定義了瀏覽器中的 Event Loop 的模型,具體實現留給了瀏覽器廠商。
libuv 是一個多平臺支援庫,主要用於非同步 I/O。它最初是為 Node.js 開發的,現在 Luvit、Julia、pyuv 和其他的框架也使用它。Github - libuv 倉庫
恍然大悟,的確是不一樣的啊!
所以,我們們得將這兩個 Event Loop 區分開來,它們是不一樣的東東哈~
最後,我們們解疑開頭的兩個問題,為什麼會這樣子,有沒辦法解決?
- 疑惑一:為什麼這份程式碼它不按照我的意思走?為啥不是輸出
1 2 3
?
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 1000);
}
// console:
// 3
// 3
// 3
複製程式碼
這道題是面試常備題,它是個很有意思的問題,不僅可以讓面試官跟你閒聊到 Event Loop,也可以閒聊下 var let const
。
為此,jsliang 特意錄製了一個 GIF,希望能幫助小夥伴進一步探索這個機制:
軟體是 VS Code,除錯方式是 Node.js
請仔細觀看 GIF 圖:
- 在執行
for
遍歷的時候,它先執行了和setTimeout
同級的console
,然後往下執行,到setTimeout
的時候,跳過了(放到某個位置)setTimeout
,依次列印了0, 1, 2
。 - 步驟 1 跳過的三次
setTimeout
開始執行,但是這時候的i
的值,經過前面的i++
後,變成了3
(for
中止迴圈後,i
已經是3
了)。所以,再依次列印了3 3 3
。
就是說,先走了正常的 for
,然後碰到 setTimeout
時,將 setTimeout
依次放到了異次元,最後走完 for
後,再將異次元中的的 setTimeout
放出,依次將數字給輸出了。
這個執行機制,就是 Event Loop 的影響,恍然大悟有木有~
這個問題的精妙之處在於,它不僅可以問你關於 Event Loop 的部分,還可以考察你對於 ES6 的 let
和 ES5 的 var
的區分,因為它有一個解決方式就是使用了 ES6 的 let
。
解決這個問題之前,不妨思考下下面的輸出:
for (var i = 0; i < 3; i++) {
}
for (let j = 0; j < 3; j++) {
}
console.log(i);
console.log(j);
複製程式碼
如果小夥伴對 ES6 有些許瞭解,應該不難猜出:
3
ReferenceError: j is not defined
複製程式碼
是不是有些想法,那麼我們們再看下下面的解決方法,再進行總結:
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 1000);
}
// console:
// 0
// 1
// 2
複製程式碼
是的,將 var i
改成了 let i
後,輸出的結果依次是 0 1 2
了。
為什麼呢?簡單回覆就是:
let
在 for
中形成了獨特的作用域塊,當前的 i
只在本輪迴圈中有效,然後 setTimeout
會找到本輪最接近的 i
,從而作出了正確的輸出。
而我們通過 var
進行的定義,它會汙染全域性變數,所以在 for
外層,還可以看到 i
的值。
當然,講到這裡,你可能還是不太清楚更細節的區分,亦或者面試官進一步問你 var let const
的區分了,你要怎麼更好回答?
看看阮一峰大佬的 ES6 文件吧:es6.ruanyifeng.com/#docs/let
這裡就不哆嗦了,有空我再將 ES6 這塊內容整理到我的文件庫中,歡迎持續關注 jsliang 的文件庫:github.com/LiangJunron…
- 疑惑二:為什麼這份程式碼它也不按照我的意思走?為啥不是輸出
樑峻榮
?
let name;
setTimeout(() => {
name = 'jsliang';
console.log(name);
}, 1000);
if (name) {
name = '樑峻榮';
console.log(name);
}
// console: 'jsliang'
複製程式碼
當你瞭解產生疑惑一的原因後,疑惑二也就不破而解了。
我們希望的是 JavaScript 按照我們需要的順序寫,結果它並沒有,就是因為受到了 Event Loop 的影響。
JavaScript 在碰到 setTimeout
的時候,會將它封印進異次元,只有等所有正常的語句(if
、for
……)執行完畢後,才會將它從異次元解封,輸出最終結果。
咦,這就有意思了,瀏覽器的異次元和 Node.js 的異次元都是怎樣的呢?我們一起往下看。
四 瀏覽器 Event Loop
在講解瀏覽器的 Event Loop 前,我們需要先了解一下 JavaScript 的執行機制:
- 所有同步任務都在主執行緒上執行,形成一個 “執行棧”(execution context stack)。
- 主執行緒之外,存在一個 “任務佇列”(task queue),在走主流程的時候,如果碰到非同步任務,那麼就在 “任務佇列” 中放置這個非同步任務。
- 一旦 “執行棧” 中所有同步任務執行完畢,系統就會讀取 “任務佇列”,看看裡面存在哪些事件。那些對應的非同步任務,結束等待狀態,進入執行棧,開始執行。
- 主執行緒不斷重複上面三個步驟。
而 JavaScript 的非同步任務,還細分兩種任務:
- 巨集任務(Macrotask):
script
(整體程式碼)、setTimeout
、setInterval
、XMLHttpRequest.prototype.onload
、I/O
、UI 渲染 - 微任務(Microtask):
Promise
、MutationObserver
這麼講是不太容易理解的,我們們上圖:
圖較大,如果是公眾號看的小夥伴,可以點選【閱讀原文】看全圖
好的,如果小夥伴們看不清楚,那麼我們們還是通過程式碼來進行講解,畢竟以上屬於 jsliang 個人理解,是從 15 篇以上文章和自己觀察程式碼執行總結出來的。
4.1 示例 1
那麼,上程式碼~
示例 1
// 位置 1
setTimeout(function () {
console.log('timeout1');
}, 1000);
// 位置 2
console.log('start');
// 位置 3
Promise.resolve().then(function () {
// 位置 5
console.log('promise1');
// 位置 6
Promise.resolve().then(function () {
console.log('promise2');
});
// 位置 7
setTimeout(function () {
// 位置 8
Promise.resolve().then(function () {
console.log('promise3');
});
// 位置 9
console.log('timeout2')
}, 0);
});
// 位置 4
console.log('done');
複製程式碼
提問:請指出上面程式碼的輸出結果?
回答:
這是經典的面試題型,所以我們們看到不用慌,先拿我們上面的點,區分下分巨集任務和微任務:
- 巨集任務(Macrotask):
script
(整體程式碼)、setTimeout
、setInterval
、XMLHttpRequest.prototype.onload
、I/O
、UI 渲染 - 微任務(Microtask):
Promise
、MutationObserver
OK,開始走流程:
如果你覺得文字不好理解,請往下翻,有 GIF 圖演示!!!
- 首先碰到的是
script
(整體程式碼),先看【位置 1】,屬於巨集任務setTimeout
下的,所以做個標記,待會回來執行。 - 接著碰到【位置 2】,這是
script
(整體程式碼)下的無阻礙程式碼,直接執行即可。 - 再來碰到【位置 3】,它現在是
script
(整體程式碼)下的微任務,所以我們們做個標記,走完檔案所有程式碼後,優先執行微任務,再執行巨集任務。 - 最後碰到【位置 4】,它是
script
(整體程式碼)下的無阻礙程式碼,直接執行即可。
這樣,第一波步驟,我們輸出的是【位置 2】的 start
和【位置 4】的 done
。
我們接著走:
- 上面我們走完了第一遍程式碼,然後現在這一步先走
script
(整體程式碼)下的微任務,即【位置 3】- 先碰到【位置 5】,這是無阻礙程式碼,直接執行。
- 再碰到【位置 6】,這是微任務,標記一下,等下執行完【位置 3】內所有程式碼後,優先執行它。
- 最後碰到【位置 7】,這是巨集任務,丟入任務佇列,看它和【位置 1】誰先走了。
- 走完一遍【位置 3】後,發現還有微任務【位置 6】,所以執行【位置 6】,進行列印輸出。
到這一步,我們就走完了 script
(整體程式碼)及之下的所有微任務了。
這時候,我們會說,【位置 1】和【位置 7】都被丟到任務佇列了,是不是【位置 1】先走呢?
答案為:不是的。
同樣的 setTimeout
,jsliang 在測試的時候,就發現它們的輸出結果在各個環境都有自己的流程,有時候先走【位置 7】,再走【位置 1】;而有時候先走【位置 1】,再走【位置 7】。
當然,如果你指定是在 Chrome
的控制檯輸出一下上面的程式碼,那就是先【位置 7】,再【位置 1】~
- point:不要主觀臆斷某個程式碼會怎麼走,最好還是直接實況執行走一波!
- 先走【位置 7】。碰到【位置 8】,將其新增到【位置 7】的微任務中,等【位置 7】所有程式碼執行完畢回來優先走微任務;碰到【位置 9】,這是無阻礙程式碼,直接輸出即可。
- 執行【位置 7】的微任務【位置 8】,輸出對應文字。
- 最後走【位置 1】,輸出對應文字。
所以答案是:
start
done
promise1
promise2
timeout2
promise3
timeout1
複製程式碼
你猜對沒有?
沒有可以看下 GIF 圖加深印象:
4.2 示例 2
在上面,jsliang 花費了許多口水,講了一些繁雜冗餘的步驟,所以下面這個示例,請小夥伴們先自行猜設,得出結論後再翻看答案和除錯 GIF~
示例 2
console.log("script start");
setTimeout(function() {
console.log("setTimeout---0");
}, 0);
setTimeout(function() {
console.log("setTimeout---200");
setTimeout(function() {
console.log("inner-setTimeout---0");
});
Promise.resolve().then(function() {
console.log("promise5");
});
}, 200);
Promise.resolve()
.then(function() {
console.log("promise1");
})
.then(function() {
console.log("promise2");
});
Promise.resolve().then(function() {
console.log("promise3");
});
console.log("script end");
複製程式碼
- 輸出結果:
script start
script end
promise1
promise3
promise2
setTimeout---0
setTimeout---200
promise5
inner-setTimeout---0
複製程式碼
- GIF 演示:
4.3 示例 3
最後再看一個示例:
示例 3
setTimeout(function() {
console.log(4);
}, 0);
const promise = new Promise(function executor(resolve) {
console.log(1);
for (var i = 0; i < 10000; i++) {
i == 9999 && resolve();
}
console.log(2);
}).then(function() {
console.log(5);
});
console.log(3);
複製程式碼
- 輸出結果:
1
2
3
5
4
複製程式碼
如果不常用 Promise
的小夥伴,可能對此感到疑惑,為啥不是:3 1 2 5 4
?
手動滑稽,別問,問就是進一步探索 Promise
:
- 《jsliang 的 Promise 探索》:github.com/LiangJunron…
當然,還沒將所有探索結果更新,如果有小夥伴催更會加快速度,歡迎留言或者私聊催更,哈哈~
4.4 小結
這樣,我們就通過 3 個示例,大致瞭解了瀏覽器的 Event Loop。
當然,實際應用中的程式碼,何止這麼簡單,甚至有時候,面試官給你的面試題,也會讓你瞠目結舌。
所以,這裡我們們廢話兩點:
- 你可以瞭解巨集任務和微任務的大體執行,例如先走
if...else...
,再走Promise
……但是,詳細到每個point
都記下來,這裡不推薦。大人,時代在進步,記住死的不如多在業務實踐中嘗試,取最新的知識。 - 瀏覽器的 Event Loop 和 Node.js 的 Event Loop 不同,萬一哪天 XX 小程式搞另類,有自己的 Event Loop,你要一一記住嗎?
碰到問題不要慌,程式設計師,折騰就對了~
五 Node.js Event Loop
那麼,下面我們們吐槽下 Node.js 的 Event Loop。
說實話,看完 Node 官網和大佬們關於 Node.js 的 Event Loop 講解,讓我想起了 Vue、React、微信小程式 的【生命週期】,再聯想到我們的人生彷彿就像被寫死的程式一樣週期性、事件性執行,非常可惡,哈哈~
上面我們講解過:Node.js 的 Event Loop 是基於 libuv。libuv 已經對 Event Loop 作出了實現。
那麼其機制是怎樣子的呢?看圖:
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
複製程式碼
關於這 6 個階段,官網描述為:
- 定時器(timers):本階段執行已經被
setTimeout()
和setInterval()
的排程回撥函式。 - 待定回撥(pending callbacks):執行延遲到下一個迴圈迭代的 I/O 回撥。
- idle, prepare:僅系統內部使用。
- 輪詢(poll):檢索新的 I/O 事件;執行與 I/O 相關的回撥(幾乎所有情況下,除了關閉的回撥函式,那些由計時器和
setImmediate()
排程的之外),其餘情況Node
將在適當的時候在此阻塞。 - 檢測(check):
setImmediate()
回撥函式在這裡執行。 - 關閉的回撥函式(close callbacks):一些關閉的回撥函式,如:
socket.on('close', ...)
。
當然,這裡 jsliang 並不想畫蛇添足,將官網或者其他大佬的文章照搬過來說是自己的,推薦小夥伴們閱讀官閘道器於 Event Loop 的各個階段的描述,以期在工作中有所使用:
- 《Node 官方講解 Event Loop》:nodejs.org/zh-cn/docs/…
Node.js 在不停的探索中,也會有所更新,所以正應了 jsliang 在瀏覽器 Event Loop 中的小結所說:不要限定死自己的知識點,與時俱進才是王道。
Node.js v9.5.0 Event Loop
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
複製程式碼
但是,迫於生活所需,有些時候,前端面試官還是會跟你扯 setTimeout & setImmediate
和 process.nextTice()
。
5.1 setTimeout & setImmediate
- setTimeout:眾所周知,這是一個定時器,指定
n
毫秒後執行定時器裡面的內容。 - setImmediate:Node.js 發現使用
setTimeout
和setInterval
有些小弊端,所以設計了個setImmediate
,該方法被設計為一旦在當前輪詢階段完成,就執行這個指令碼。
當然,光說無益,看程式碼:
index.js
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
複製程式碼
猜測下在 VS Code 中執行 node index.js
命令會發生什麼?
結局 1
immediate
timeout
複製程式碼
結局 2
timeout
immediate
複製程式碼
事實上這兩個結局都是會存在的,看似 happy ending,但是有的小夥伴可能心裡鬧翻天。
按照官網的解釋:
- 執行計時器的順序將根據呼叫它們的上下文而異。
- 如果兩則都從主模組內呼叫,則計時器將受到程式效能的約束(這可能會受到計算機上其他正在執行應用程式的影響)。
- 如果你將這兩個函式放入一個 I/O 迴圈內呼叫,
setImmediate
總是被有限呼叫。
const fs = require('fs');
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
});
複製程式碼
雖然官方解釋的很 巧妙,但是不管你懂不懂,反正我覺得有點扯淡。
最後再來句官方總結:
- 使用
setImmediate()
相對於setTimeout
的主要優勢是:如果setImmediate()
是在 I/O 週期內被排程的,那麼它將會在任何的定時器之前執行,跟這裡存在多少個定時器無關。
enm...後面如果我具體使用 Node.js 的時候,我再進一步觀察吧,至於現在,我還是先了解下即可。
5.2 process.nextTick()
nextTick
比較特殊,它存有自己的佇列。
並且,它獨立於 Event Loop,無論 Event Loop 處於何種階段,都會在階段結束的時候清空 nextTick
佇列。
還有需要注意的是:process.nextTick()
優先於其他的微任務(microtask)執行。
當然,如果你對此有所興趣,你可以進一步探索原始碼,或者觀察大佬們探索原始碼:
沒有使用就沒有發言權,作為一個 Node.js 菜雞,這裡就不妄加評論分析了。
5.3 示例 1
下面開始示例,我們看下 Node.js 的 Event Loop 有何差異:
示例 1
setTimeout(() => {
console.log("timer1");
Promise.resolve().then(function() {
console.log("promise1");
});
});
setTimeout(() => {
console.log("timer2");
Promise.resolve().then(function() {
console.log("promise2");
});
});
複製程式碼
如果你還記得上面講解的瀏覽器的 Event Loop,你可能會將答案直接寫成:
瀏覽器 Event Loop 輸出:
timer1
promise1
timer2
promise2
複製程式碼
是的你是對的,那就是瀏覽器的 Event Loop,到了 Node.js 這塊,就有不同變化了:
Node.js Event Loop 輸出:
timer1
timer2
promise1
promise2
複製程式碼
嘗試接受它!
然後大聲默唸:根據具體環境進行對應觀察和得出結論。
5.4 示例 2
下面我們們再看一個示例:
示例 2
setTimeout(function () {
console.log(1);
});
console.log(2);
process.nextTick(() => {
console.log(3);
});
new Promise(function (resolve, rejected) {
console.log(4);
resolve()
}).then(res=>{
console.log(5);
})
setImmediate(function () {
console.log(6)
})
console.log('end');
複製程式碼
node index.js
2
4
end
3
5
1
6
複製程式碼
這裡不打算解析,因為我怕初識 Event Loop 的小夥伴看完解釋後懵逼,然後搞混淆了。
實話:我也不敢解析,因為我就是 Node.js 菜雞
5.5 小結
終上所述,我們進行小結:
Node 端事件迴圈中的非同步佇列也是這兩種:Macrotask(巨集任務)佇列和 Microtask(微任務)佇列。
- 常見的 Macrotask:
setTimeout
、setInterval
、setImmediate
、script
(整體程式碼)、 I/O 操作等。 - 常見的 Microtask:
process.nextTick
、new Promise().then(回撥)
等。
OK,我們們就探索了一遍 Node.js 的 Event Loop 啦,但是因為我們還成就不了 Node.js 工程師,所以我們就不對其進行詳細探索,以免和瀏覽器的 Event Loop 混淆了。
感興趣的小夥伴可以自行探索咯~
六 總結
如果你看到這裡,你已經近乎懵逼,那麼,還是那個建議:
- 不管 Event Loop 在瀏覽器亦或者 Node.js 表現機制,最好的操作還是在對應環境中進行嘗試。
你不能完全保證你的記憶力是 OK 的,所以你只需要知道有這個問題,然後在工作中實踐解決即可。
enm...所以你看完了一篇水文,唯一的作用是讓你面試的時候,能愉快地玩耍一些簡單題目~
哈哈,Good luck.
如果你覺得我的文章還不錯,想持續關注或者加我微信好友,歡迎前往 github.com/LiangJunron… 進行 star 或者加微信。
七 參考文獻
感謝以下大佬們的文章,讓我受益頗多。
並在他們創作的基礎上,基於自己的想法,進行了整合。
- 《Tasks, microtasks, queues and schedules》 - Jake
- 《徹底搞懂瀏覽器 Event-loop》 - 劉小夕
- 《徹底理解 JS Event Loop(瀏覽器環境)》 - 93
- 《徹底弄懂瀏覽器端的 Event-Loop》 - 長可
- 《什麼是瀏覽器的事件迴圈(Event Loop)?》 - 魚子醬
- 《理解event loop(瀏覽器環境與nodejs環境)》 - sugerpocket
- 《從 event loop 規範探究 JavaScript 非同步及瀏覽器更新渲染時機》 - 楊敬卓
- 《跟著 Event loop 規範理解瀏覽器中的非同步機制》 - fi3ework
- 《不要混淆 nodejs 和瀏覽器中的 event loop》 - youth7
- 《瀏覽器的 event loop 和 node 的 event loop》 - 金大光
- 《瀏覽器與 Node 的事件迴圈(Event Loop)有何區別?》 - 浪裡行舟
- 《瀏覽器和 Node 不同的事件迴圈(Event Loop)》 - toBeTheLight
- 《let 和 const 命令》 - 阮一峰
- 《Node.js Event Loop》 - Node.js 官網
不折騰的前端,和鹹魚有什麼區別!
jsliang 會每天更新一道 LeetCode 題解,從而幫助小夥伴們夯實原生 JS 基礎,瞭解與學習演算法與資料結構。
浪子神劍 會每天更新面試題,以面試題為驅動來帶動大家學習,堅持每天學習與思考,每天進步一點!
掃描上方二維碼,關注 jsliang 的公眾號(左)和 浪子神劍 的公眾號(右),讓我們一起折騰!
jsliang 的文件庫 由 樑峻榮 採用 知識共享 署名-非商業性使用-相同方式共享 4.0 國際 許可協議進行許可。
基於github.com/LiangJunron…上的作品創作。
本許可協議授權之外的使用許可權可以從 creativecommons.org/licenses/by… 處獲得。