js 運行機制
javascript的運行機制
- 單線程
- 任務隊列
- 事件和回調函數
- 異步IO
javascript
最大的特點就是單線程,也就是在同一時間只能做一件事情.那為什麽會是單線程呢?這還要從javascript的用途來看.javascript的主要用途就是與用戶互動以及DOM操作(瀏覽器中),這就決定的了它必須是單線程的,不然會出現很嚴重的問題.比如當有兩個線程同時對一個DOM進行操作的時候,瀏覽器不知道到底以哪個線程為準.既然是單線程的,又怎樣保證性能呢?接下來我們就來看看javascript的運行機制
因為nodejs的出現,js不再只是運行在瀏覽器端,還可以運行在服務端,所有我們就分別從這兩個方面去考慮
瀏覽器端
上面說到js是單線程的,那是不是意味著所有的任務就需要排成一個隊列,每個任務都必須等待上一個任務完成才能繼續下去.如果上一個任務很耗時間,那我們就需要一直等.顯然這是不合理的
在js中I/O操作是很慢的(比如ajax操作就需要從網絡中獲取數據),但是此時CPU卻是非常空閑的.所以這個時候,js會掛起這個I/O操作(我們稱為異步),繼續執行下面的代碼,等到IO操作結束後返回結果,再把掛起的任務繼續執行下去
任務隊列(可以有多個)
- macrotask
- microtask
前面我們說js
是單線程的,但是在主線程之外,還維持著一個任務隊列
.每當js去執行代碼的時候(同步),遇見異步
任務的時候,不會等待主線程去執行它,會把這個任務加入任務隊列
任務隊列
通知主線程,這個異步隊列
可以執行了,該任務才會進入主線程執行,這個過程可以按照如下表述
- 所有同步操作都是在主線程上操作,形成一個執行棧
- 主線程之外,還存在一個
任務隊列
.只要異步任務有了運行結果,就在任務隊列
之中放置一個事件 - 一旦
執行棧
中所有同步任務執行完畢,系統就會讀取異步隊列
,然後對應的事件就會進入執行棧,開始執行.主線程不斷執行這個操作,知道隊列清空
事件循環(只有一個)和回調函數
"任務隊列"是一個事件的隊列(也可以理解成消息的隊列),IO設備完成一項任務,就在"任務隊列"中添加一個事件,表示相關的異步任務可以進入"執行棧"了。主線程讀取"任務隊列",就是讀取裏面有哪些事件。
"任務隊列"中的事件,除了IO設備的事件以外,還包括一些用戶產生的事件(比如鼠標點擊、頁面滾動等等).只要指定過回調函數,這些事件發生時就會進入"任務隊列",等待主線程讀取。
所謂"回調函數"(callback),就是那些會被主線程掛起來的代碼。異步任務必須指定回調函數,當主線程開始執行異步任務,就是執行對應的回調函數。
"任務隊列"是一個先進先出的數據結構,排在前面的事件,優先被主線程讀取。主線程的讀取過程基本上是自動的,只要執行棧一清空,"任務隊列"上第一位的事件就自動進入主線程。但是,由於存在後文提到的"定時器"功能,主線程首先要檢查一下執行時間,某些事件只有到了規定的時間,才能返回主線程。
定時器
除了放置異步任務的事件,"任務隊列"還可以放置定時事件,即指定某些代碼在多少時間之後執行。這叫做"定時器"功能,也就是定時執行的代碼。
定時器功能主要由setTimeout()和setInterval()這兩個函數來完成,它們的內部運行機制完全一樣,區別在於前者指定的代碼是一次性執行,後者則為反復執行
需要註意的是,setTimeout()只是將事件插入了"任務隊列",必須等到當前代碼(執行棧)執行完,主線程才會去執行它指定的回調函數。要是當前代碼耗時很長,有可能要等很久,所以並沒有辦法保證.
JS中的異步操作:
1、定時器都是異步操作
2、事件綁定都是異步操作
3、AJAX中一般我們都采取異步操作(也可以同步)
4、回調函數可以理解為異步(不是嚴謹的異步操作)
js 運行機制