學習javascipt的時候,常常聽人說,javascipt便是異步的,又是單線程的。究竟什麼是異步,什麼是單線程?javascript在瀏覽器中的運行機制是怎麼樣的?什麼是eventloop,task queue?怎麼寫異步函數?相信讀完這篇文章,相信你會對上面問題有一個全面的認識。javascript
主線程
)雖然JavaScript是單線程的(說的是JS引擎線程),但是瀏覽器內部不是單線程的。一些I/O操做、定時器的計時和事件監聽(click, keydown...)等都是由瀏覽器提供的其餘線程來完成的。
主線程和渲染引擎線程互斥,由於渲染的時候主線程可能經過dom操做渲染結果,因此主線程必須被阻塞
html
以前傻傻的分不清楚單線程多線程,同步異步。其實很簡單java
異步的判斷標準:是否阻塞,同步阻塞,異步不阻塞。node
單線程的判斷標準:一次是否只作一件事。面試
JS引擎一次只作一件事。遇到異步任務並不會阻塞後面的同步任務(不等待)。因此咱們說JS是異步 單線程的。須要注意的是JS引擎其實並不提供異步的支持,異步支持主要依賴於運行環境(瀏覽器或Node.js)。
chrome
var start = new Date(); while(new Date() - start < 100000) { // delay 10 sec ; }
上面代碼在chrome控制檯輸入能夠手動阻塞當前頁面的js主線程10s。而後咱們在當前頁面輸入console.log(1),當前頁面無反應,在另外的頁面輸入console.log(1)直接打印
說明瀏覽器每一個頁面都會單獨起一個進程,頁面1的主線程被阻塞並不會影響影響頁面2的主線程
編程
以前在Stackoverflow看了一個答案,感受還比較靠譜promise
JavaScript Engine:parse your code and convert it to runnable commands
JavaScript Runtime Environment :provide some objects to javascript so that it can interact with the outside world.
For example, the Chrome Browser and node.js use the same Engine - V8, but their Runtimes are different: in Chrome you have the window, DOM objects etc, while node gives you require, Buffers and processes.
通俗的講,上面這張圖,左邊你能夠當作JS引擎,右邊你能夠當作JS運行環境瀏覽器
以前已經說了,JS在設計之初選擇單線程,是覺得單線程簡單,可控。session
可是單線程存在一個問題,部分任務好比Ajax請求數據,若是設計成同步的,後面的任務將都去等待Ajax請求完,這個性能是不能接受的。
因此瀏覽器內核(?我的推測,暫時沒有找到相關資料
)將任務分爲同步任務和異步任務,全部同步任務放到主線程上執行,造成一個執行棧(execution context stack)。因此異步任務放到其餘異步線程上去執行。
當異步任務執行完之後,相關回調函數會放入到消息隊列(也有叫callback queue、task queue)中。
主線程同步任務執行完,每一個一段事件會檢查消息隊列一次,有回調函數就會執行,如此往復就成爲Eventloop
我的的理解 :JS引擎是同步的,瀏覽器經過eventloop這種機制實現了異步
看一下How JavaScript works 怎麼描述這個過程的
So, for example, when your JavaScript program makes an Ajax request to fetch some data from the server, you set up the 「response」 code in a function (the 「callback」), and the JS Engine tells the hosting environment:
「Hey, I’m going tosuspend
execution for now, but whenever you finish with that network request, and you have some data, please call this function back.」
The browser
is then set up tolisten
for the response from the network, and when it has something to return to you,it will schedule the callback function to be executed by inserting it into the event loop
.
面試喜歡考宏任務(macrotask),微任務(microtask)。那麼咱們就來說一講macrotask和microtask是個啥子
宏任務又成爲task。能夠理解是每次執行棧執行的代碼就是一個task
,task1->渲染->task1
microtask,能夠理解是在當前
task 執行結束後當即執行的任務,因此microtask有歸屬性,只在對應的task執行完後當即執行.task1->microtask1->渲染->task2->microtask2->渲染...
macrotask:主代碼塊,setTimeout,setInterval等(能夠看到,事件隊列中的每個事件都是一個macrotask)microtask:Promise,process.nextTick等
求下面代碼的結果
console.log('1'); setTimeout(function() { //回調2 new Promise(function(resolve) { console.log('2'); resolve(); }).then(function() { console.log('3') }) console.log('4'); },2000) new Promise(function(resolve) { console.log('5'); resolve(); }).then(function() { console.log('6') }) setTimeout(function() { //回調1 new Promise(function(resolve) { console.log('7'); resolve(); }).then(function() { console.log('8') }) setTimeout(function(){ //回調3 console.log('9') },2000) },1000) //(156) (78) (243) (9)
解析:
task1: 輸出1 5
----> microtask1 輸出6
--(執行棧空)-->render---->eventloop1秒之後
callback queue裏面加入回調1
被eventloop捕獲,同步任務入棧,異步任務給settiomeout線程(也就是回調3的那個異步任務)
task2: 輸出7
----> microtask2 輸出8
--(執行棧空)-->render---->eventloop2秒之後
callback queue裏面加入回調2
被eventloop捕獲,同步任務入棧
task3: 輸出2 4
----> microtask3 輸出3
--(執行棧空)-->render---->eventloop3秒之後
callback queue裏面加入回調3
被eventloop捕獲,同步任務入棧
task4: 輸出9
--(執行棧空)-->render---->eventloop...
let fs = require('fs'); fs.readFile('./1.js','utf-8',(err,data)=>{ // fs.readFile('./2.js','utf-8',(err,data)=>{ // fs.readFile('./3.js','utf-8',(err,data)=>{ // }) }) })
缺點是容易造成回調地獄,不能return
const fs = require('fs'); const readFile(i) = new Promise(function(){ fs.readFile(`./${i}.js`,'utf-8',(err,data)=>{ resolve(data) }) }) readFile(1) .then(readFile(2)) .then(readFile(3)) .....
async function read(){ //await後面必須跟一個promise, let a = await readFile('./1.txt'); console.log(a); let b = await readFile('./2.txt'); console.log(b); let c = await readFile('./3.txt'); console.log(c); return 'end'; }
以上是我看了多篇文章之後,結合本身的理解,對javascript異步單線程,以及運行機制作的一個總結。若是你感受哪一部分有點問題,歡迎在評論區留言。
從瀏覽器多進程到JS單線程,JS運行機制最全面的一次梳理
JavaScript 運行機制詳解:再談Event Loop
JavaScript異步機制詳解
JavaScript 運行原理解析
What is the difference between JavaScript Engine and JavaScript Runtime Environment
併發模型與事件循環