摘要:異步如果在函數(shù)返回的時候,調(diào)用者還不能購得到預(yù)期結(jié)果,而是將來通過一定的手段得到例如回調(diào)函數(shù),這就是異步。的意思是,將回調(diào)函數(shù)立刻插入消息隊列,等待執(zhí)行,而不是立即執(zhí)行。
大家好,我是wmingren,小伙伴們都知道JavaScript是單線程的語言,所謂的單線程呢就是指如果有多個任務(wù)就必須去排隊,前面任務(wù)執(zhí)行完成后,后面任務(wù)再執(zhí)行。到這里我們就產(chǎn)生了一個疑問,既然是單線程的,又怎么會有異步操作呢?首先了解一下同步和異步的概念吧。一、同步和異步 同步
如果在函數(shù)返回結(jié)果的時候,調(diào)用者能夠拿到預(yù)期的結(jié)果(就是函數(shù)計算的結(jié)果),那么這個函數(shù)就是同步的.
console.log("hello");//執(zhí)行后,獲得了返回結(jié)果
如果函數(shù)是同步的,即使調(diào)用函數(shù)執(zhí)行任務(wù)比較耗時,也會一致等待直到得到執(zhí)行結(jié)果。如下面的代碼:
function wait(){ var time = (new Date()).getTime();//獲取當(dāng)前的unix時間戳 while((new Date()).getTime() - time > 5000){} console.log("5秒過去了"); } wait(); console.log("慢死了");
上面代碼中,函數(shù)wait是一個耗時程序,持續(xù)5秒,在它執(zhí)行的這漫長的5秒中,下面的console.log()函數(shù)只能等待,這就是同步。
異步如果在函數(shù)返回的時候,調(diào)用者還不能購得到預(yù)期結(jié)果,而是將來通過一定的手段得到(例如回調(diào)函數(shù)),這就是異步。例如ajax操作。二、單線程與多線程
如果函數(shù)是異步的,發(fā)出調(diào)用之后,馬上返回,但是不會馬上返回預(yù)期結(jié)果。調(diào)用者不必主動等待,當(dāng)被調(diào)用者得到結(jié)果之后會通過回調(diào)函數(shù)主動通知調(diào)用者。
了解完同步和異步之后,我們再來看看我們的問題:單線程又怎么會有異步呢?瀏覽器
JavaScript其實就是一門語言,說是單線程還是多線程得結(jié)合具體運行環(huán)境。眾所周知,js的運行環(huán)境就是瀏覽器,具體由js引擎取解析和執(zhí)行。下面我們來了解下瀏覽器。
一個瀏覽器通常由以下幾個常駐的線程:
渲染引擎線程,負(fù)責(zé)頁面的渲染
js引擎線程,負(fù)責(zé)js的解析和執(zhí)行
定時觸發(fā)器線程,處理setInterval和setTimeout
事件觸發(fā)線程,處理DOM事件
異步http請求線程,處理http請求
要注意的是渲染引擎和js引擎線程是不能同時進行的。渲染線程在執(zhí)行任務(wù)的時候,js引擎線程會被掛起。因為若是在渲染頁面的時候,js處理了DOM,瀏覽器就不知道該聽誰的了JS引擎
通常講到瀏覽器的時候,我們會說到兩個引擎:渲染引擎和JS引擎。
1、渲染引擎:Chrome/Safari/Opera用的是Webkit引擎,IE用的是Trdent引擎,F(xiàn)ireFox用的是Gecko引擎。不同的引擎對同一個樣式的實現(xiàn)不一致,就導(dǎo)致瀏覽器的兼容性問題。
2、JS引擎:js引擎可以說是js虛擬機,負(fù)責(zé)解析js代碼的解析和執(zhí)行。通常有以下步驟:
詞法解析:將源代碼分解位有意義的分詞
語法分析:用語法分析器將分詞解析成語法樹
代碼生成:生成機器能運行的代碼
代碼執(zhí)行
不同瀏覽器的js引擎也各不相同,Chrome用的是V8,F(xiàn)ireFox用的是SpiderMonkey,Safari用的是JavaScriptCore,IE用的是Chakra。三、消息隊列與事件循環(huán)之所以說js是單線程就是因為瀏覽器運行時只開啟一個js解釋器,原因是若有兩個線程操作DOM,瀏覽器就又暈了。
JavaScript是單線程的,但是瀏覽器不是單線程的。一些I/O操作,定時器的計時和事件監(jiān)聽是由其他線程完成的。
由上面瀏覽器一篇的介紹可以知道,瀏覽器中多個線程的合作完成了異步的操作,那么異步的回調(diào)函數(shù)又是怎樣完成執(zhí)行的呢?
這就需要了解消息隊列和事件循環(huán)了。
如上圖所示,左邊的棧存儲的是同步任務(wù),就是那些能立即執(zhí)行、不耗時的任務(wù),如變量和函數(shù)的初始化、事件的綁定等等那些不需要回調(diào)函數(shù)的操作都可歸為這一類。四、實例右邊的堆用來存儲聲明的變量、對象。下面的隊列就是消息隊列,一旦某個異步任務(wù)有了響應(yīng)就會被推入隊列中。如用戶的點擊事件、瀏覽器收到服務(wù)的響應(yīng)和setTimeout中待執(zhí)行的事件,每個異步任務(wù)都和回調(diào)函數(shù)相關(guān)聯(lián)。
JS引擎線程用來執(zhí)行棧中的同步任務(wù),當(dāng)所有同步任務(wù)執(zhí)行完畢后,棧被清空,然后讀取消息隊列中的一個待處理任務(wù),并把相關(guān)回調(diào)函數(shù)壓入棧中,單線程開始執(zhí)行新的同步任務(wù)。
JS引擎線程從消息隊列中讀取任務(wù)是不斷循環(huán)的,每次棧被清空后,都會在消息隊列中讀取新的任務(wù),如果沒有新的任務(wù),就會等待,直到有新的任務(wù),這就叫事件循環(huán)。
上圖以AJAX異步請求為例,發(fā)起異步任務(wù)后,由AJAX線程執(zhí)行耗時的異步操作,而JS引擎線程繼續(xù)執(zhí)行堆中的其他同步任務(wù),直到堆中的所有異步任務(wù)執(zhí)行完畢。然后,從消息隊列中依次按照順序取出消息作為一個同步任務(wù)在JS引擎線程中執(zhí)行,那么AJAX的回調(diào)函數(shù)就會在某一時刻被調(diào)用執(zhí)行。
最后來一個經(jīng)典的面試題來幫助大家理解js的同步和異步。
代碼如下:
//執(zhí)行下面這段代碼,執(zhí)行后,在 5s 內(nèi)點擊兩下,過一段時間(>5s)后,再點擊兩下,整個過程的輸出結(jié)果是什么? setTimeout(function(){ for(var i = 0; i < 100000000; i++){} console.log("timer a"); }, 0) for(var j = 0; j < 5; j++){ console.log(j); } setTimeout(function(){ console.log("timer b"); }, 0) function waitFiveSeconds(){ var now = (new Date()).getTime(); while(((new Date()).getTime() - now) < 5000){} console.log("finished waiting"); } document.addEventListener("click", function(){ console.log("click"); }) console.log("click begin"); waitFiveSeconds();
要想了解上述代碼的輸出結(jié)果,首先介紹下定時器。
setTimeout 的作用是在間隔一定的時間后,將回調(diào)函數(shù)插入消息隊列中,等棧中的同步任務(wù)都執(zhí)行完畢后,再執(zhí)行。因為棧中的同步任務(wù)也會耗時, 所以間隔的時間一般會大于等于指定的時間 。setTimeout(fn, 0) 的意思是,將回調(diào)函數(shù)fn立刻插入消息隊列,等待執(zhí)行,而不是立即執(zhí)行??匆粋€例子:
setTimeout(function() { console.log("a") }, 0) for(let i=0; i<10000; i++) {} console.log("b")
//打印結(jié)果,說明回調(diào)函數(shù)沒有立即執(zhí)行,而是等待同步任務(wù)執(zhí)行完成后才執(zhí)行的 b a
下面來解釋一下面試題吧。先執(zhí)行同步任務(wù),for循環(huán),然后是console.log("click begin") 最后是waitFiveSeconds函數(shù)
在同步任務(wù)執(zhí)行的期間,‘timera’,‘timerb’對應(yīng)的回調(diào)和click事件的回調(diào)先后入隊列。
同步任務(wù)結(jié)束后,js引擎線程空閑后會線查看是否有事件可執(zhí)行,接著在處理其他異步任務(wù),因此會有下面的輸出:
0 1 2 3 4 click begin finished waiting 2 click //5s中兩次點擊 timer a timer b 2 click //5s后兩次點擊
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/107056.html
摘要:今天的已經(jīng)成為一門功能全面的編程語言總結(jié)最初的用途是為來實現(xiàn)用戶與瀏覽器的交互二為何是單線程的的單線程,與它的用途有關(guān)。這決定了它只能是單線程,否則會帶來很復(fù)雜的同步問題。 showImg(https://user-gold-cdn.xitu.io/2019/3/31/169d1c40c27a173c?w=428&h=252&f=png&s=35393); 前言 我本來是打算寫一篇co...
摘要:回調(diào)函數(shù),一般在同步情境下是最后執(zhí)行的,而在異步情境下有可能不執(zhí)行,因為事件沒有被觸發(fā)或者條件不滿足。同步方式請求異步同步請求當(dāng)請求開始發(fā)送時,瀏覽器事件線程通知主線程,讓線程發(fā)送數(shù)據(jù)請求,主線程收到 一直以來都知道JavaScript是一門單線程語言,在筆試過程中不斷的遇到一些輸出結(jié)果的問題,考量的是對異步編程掌握情況。一般被問到異步的時候腦子里第一反應(yīng)就是Ajax,setTimse...
摘要:同步異步回調(diào)傻傻分不清楚。分割線上面主要講了同步和回調(diào)執(zhí)行順序的問題,接著我就舉一個包含同步異步回調(diào)的例子。同步優(yōu)先回調(diào)內(nèi)部有個,第二個是一個回調(diào)回調(diào)墊底。異步也,輪到回調(diào)的孩子們回調(diào),出來執(zhí)行了。 同步、異步、回調(diào)?傻傻分不清楚。 大家注意了,教大家一道口訣: 同步優(yōu)先、異步靠邊、回調(diào)墊底(讀起來不順) 用公式表達(dá)就是: 同步 => 異步 => 回調(diào) 這口訣有什么用呢?用來對付面試的...
摘要:同步異步回調(diào)傻傻分不清楚。分割線上面主要講了同步和回調(diào)執(zhí)行順序的問題,接著我就舉一個包含同步異步回調(diào)的例子。同步優(yōu)先回調(diào)內(nèi)部有個,第二個是一個回調(diào)回調(diào)墊底。異步也,輪到回調(diào)的孩子們回調(diào),出來執(zhí)行了。 同步、異步、回調(diào)?傻傻分不清楚。 大家注意了,教大家一道口訣: 同步優(yōu)先、異步靠邊、回調(diào)墊底(讀起來不順) 用公式表達(dá)就是: 同步 => 異步 => 回調(diào) 這口訣有什么用呢?用來對付面試的...
閱讀 3226·2021-11-23 09:51
閱讀 3569·2021-11-09 09:46
閱讀 3679·2021-11-09 09:45
閱讀 2951·2019-08-29 17:31
閱讀 1869·2019-08-26 13:39
閱讀 2729·2019-08-26 12:12
閱讀 3627·2019-08-26 12:08
閱讀 2244·2019-08-26 11:31