1、JavaScriptjavascript
咱們常說「JavaScript是單線程的」。java
所謂單線程,是指在JS引擎中負責解釋和執行JavaScript代碼的線程只有一個。能夠叫它主線程。ajax
2、同步和異步異步
同步任務指的是,在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;函數
異步任務指的是,不進入主線程、而進入"任務隊列"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行。url
異步請求執行過程:spa
主線程發起一個異步請求,相應的工做線程接收請求並告知主線程已收到(異步函數返回);主線程能夠繼續執行後面的代碼,同時工做線程執行異步任務;工做線程完成工做後,通知主線程;主線程收到通知後,執行必定的動做(調用回調函數)。線程
(1)全部同步任務都在主線程上執行,造成一個執行棧(execution context stack)。code
(2)主線程以外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。blog
(3)一旦"執行棧"中的全部同步任務執行完畢,系統就會讀取"任務隊列",看看裏面有哪些事件。那些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行。
(4)主線程不斷重複上面的第三步。
例如ajax執行過程
let data = []; $.ajax({ url:www.javascript.com, data:data, success:() => { console.log('發送成功!'); } }) console.log('代碼執行結束');
success
。console.log('代碼執行結束')
。success
進入任務隊列。success
並執行。
"任務隊列"是一個事件的隊列(也能夠理解成消息的隊列),IO設備完成一項任務,就在"任務隊列"中添加一個事件,表示相關的異步任務能夠進入"執行棧"了。主線程讀取"任務隊列",就是讀取裏面有哪些事件。
"任務隊列"中的事件,除了IO設備的事件之外,還包括一些用戶產生的事件(好比鼠標點擊、頁面滾動等等)。只要指定過回調函數,這些事件發生時就會進入"任務隊列",等待主線程讀取。
所謂"回調函數"(callback),就是那些會被主線程掛起來的代碼。異步任務必須指定回調函數,當主線程開始執行異步任務,就是執行對應的回調函數。
因爲JavaScript引擎同一時間只執行一條代碼(這是因爲JavaScript單線程的性質),因此每個JavaScript代碼執行塊會 「阻塞」其它異步事件的執行。這就意味着當一個異步事件發生(例如,鼠標點擊,計時器被觸發,或者Ajax異步請求)後,這些事件的回調函數將排在執行隊 列的最後等待執行
// some code setTimeout(function() { console.log('hello'); }, 10); // some code document.getElementById('btn').click(); // some code setInterval(function() { console.log('world'); }, 10); // some code
咱們開始執行代碼。第一塊代碼大概執行了18ms,也就是JavaScript的主體代碼,在執行過程當中,先觸發了一個setTimeout函數,代碼繼續執行,只等10ms後響應setTimeout的回調,接着是一個鼠標點擊事件,該事件有個回調(或許是alert一些東西),不能當即執行(單線程),由於js主體代碼還沒執行完,因此這個回調被插入執行隊列中,等待執行;接着setInterval函數被執行,咱們知道,此後每隔10ms都會有回調(嘗試)插入隊列中,運行到第10ms的時候,setTimeout函數的回調插入隊列。js函數主體運行完後,大概是18ms這個點,咱們發現隊列中有個click的callback,還有個setTimeout的callback,因而咱們先運行前者,在運行的過程當中,setInterval的10ms響應時間也過了,一樣回調被插入隊列。click的回調運行完,運行setTimeout的回調,這時又10ms過去了,setInterval又產生了回調,可是這個回調被拋棄了,以後發生的事你們都一目瞭然了。