欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript的單線程和異步詳細

 更新時間:2021年10月08日 09:27:22   作者:四目大大  
這篇文章要給大家分享的是JavaScript的單線程和異步,其實單線程和異步確實不能同時成為一個語言的特性,js選擇了成為單線程的語言,所以它本身不可能是異步的,但js宿主環(huán)境是多線程,宿主環(huán)境通過某種方式使js具備了異步屬性,下面就來具體介紹,需要的朋友可以參考一下

前言:

說到JavaScript的單線程(single threaded)和異步(asynchronous),很多同學不禁會想,這不是自相矛盾么?其實,單線程和異步確實不能同時成為一個語言的特性。js選擇了成為單線程的語言,所以它本身不可能是異步的,但js的宿主環(huán)境(比如瀏覽器,Node)是多線程的,宿主環(huán)境通過某種方式(事件驅(qū)動,下文會講)使得js具備了異步的屬性。往下看,你會發(fā)現(xiàn)js的機制是多么的簡單高效!

瀏覽器:

js是單線程語言,瀏覽器只分配給js一個主線程,用來執(zhí)行任務(wù)(函數(shù)),但一次只能執(zhí)行一個任務(wù),這些任務(wù)形成一個任務(wù)隊列排隊等候執(zhí)行,但前端的某些任務(wù)是非常耗時的,比如網(wǎng)絡(luò)請求,定時器和事件監(jiān)聽,如果讓他們和別的任務(wù)一樣,都老老實實的排隊等待執(zhí)行的話,執(zhí)行效率會非常的低,甚至導致頁面的假死。所以,瀏覽器為這些耗時任務(wù)開辟了另外的線程,主要包括http請求線程,瀏覽器定時觸發(fā)器,瀏覽器事件觸發(fā)線程,這些任務(wù)是異步的。下圖說明了瀏覽器的主要線程。

一、任務(wù)隊列

剛才說到瀏覽器為網(wǎng)絡(luò)請求這樣的異步任務(wù)單獨開了一個線程,那么問題來了,這些異步任務(wù)完成后,主線程怎么知道呢?答案就是回調(diào)函數(shù),整個程序是事件驅(qū)動的,每個事件都會綁定相應(yīng)的回調(diào)函數(shù),舉個栗子,有段代碼設(shè)置了一個定時器

setTimeout(function(){
    console.log(time is out);
},50);


執(zhí)行這段代碼的時候,瀏覽器異步執(zhí)行計時操作,當50ms到了后,會觸發(fā)定時事件,這個時候,就會把回調(diào)函數(shù)放到任務(wù)隊列里。整個程序就是通過這樣的一個個事件驅(qū)動起來的。
所以說,js是一直是單線程的,瀏覽器才是實現(xiàn)異步的那個家伙。

說回主線程:

js一直在做一個工作,就是從任務(wù)隊列里提取任務(wù),放到主線程里執(zhí)行。下面我們來進行更深一步的理解。

我們把剛才了解的概念和圖中做一個對應(yīng),上文中說到的瀏覽器為異步任務(wù)單獨開辟的線程可以統(tǒng)一理解為WebAPIs,上文中說到的任務(wù)隊列就是callback queue,我們所說的主線程就是有虛線組成的那一部分,堆(heap)和棧(stack)共同組成了js主線程,函數(shù)的執(zhí)行就是通過進棧和出棧實現(xiàn)的,比如圖中有一個foo()函數(shù),主線程把它推入棧中,在執(zhí)行函數(shù)體時,發(fā)現(xiàn)還需要執(zhí)行上面的那幾個函數(shù),所以又把這幾個函數(shù)推入棧中,等到函數(shù)執(zhí)行完,就讓函數(shù)出棧。等到stack清空時,說明一個任務(wù)已經(jīng)執(zhí)行完了,這時就會從callback queue中尋找下一個人任務(wù)推入棧中(這個尋找的過程,叫做event loop,因為它總是循環(huán)的查找任務(wù)隊列里是否還有任務(wù))。

二、借以解釋幾個容易困惑的問題

1、setTimeout(f1,0)是什么鬼

這個語句最大的疑問是,f1是不是立刻執(zhí)行?答案是不一定,因為要看主線程內(nèi)的命令是否已經(jīng)執(zhí)行完了,如下代碼:

setTimeout(function(){
console.log(1);
},0);
console.log(2);

2、Ajax請求是否異步

了解完上文內(nèi)容,我們就知道了,ajax請求內(nèi)容的時候是異步的,當請求完成后,會觸發(fā)請求完成的事件,然后把回調(diào)函數(shù)放入callback queue,等到主線程執(zhí)行該回調(diào)函數(shù)時還是單線程的。

3、界面渲染線程是單獨開辟的線程

界面渲染線程是單獨開辟的線程,是不是DOM一變化,界面就立刻重新渲染?

如果DOM一變化,界面就立刻重新渲染,效率必然很低,所以瀏覽器的機制規(guī)定界面渲染線程和主線程是互斥的,主線程執(zhí)行任務(wù)時,瀏覽器渲染線程處于掛起狀態(tài)。

三、如何利用瀏覽器的異步機制

我們已經(jīng)知道,js一直是單線程執(zhí)行的,瀏覽器為幾個明顯的耗時任務(wù)單獨開辟線程解決耗時問題,但是js除了這幾個明顯的耗時問題外,可能我們自己寫的程序里面也會有耗時的函數(shù),這種情況怎么處理呢?我們肯定不能自己開辟單獨的線程,但我們可以利用瀏覽器給我們開放的這幾個窗口,瀏覽器定時器線程和事件觸發(fā)線程是好利用的,網(wǎng)絡(luò)請求線程不適合我們使用。下面我們具體看一下:

假設(shè)耗時函數(shù)是f1,f1是f2的前置任務(wù)。

利用定時器觸發(fā)線程:

function f1(callback){
setTimeout(function(){
    // f1 的代碼
    callback();
},0);
}
f1(f2);

這種寫法的耦合度高。

利用事件觸發(fā)線程:

$f1.on('custom',f2);  //這里綁定事件以jQuery寫法為例
function f1(){
setTimeout(function(){
    // f1的代碼
    $f1.trigger('custom');
},0);
}


這種方法通過綁定自定義事件,對方法一解耦,這樣可以通過綁定不同的事件,實現(xiàn)不同的回調(diào)函數(shù),但如果應(yīng)用這種方法過多,不利于閱讀程序。

四、異步的好處和適合的場景

異步的好處:

我們直接通過一個例子對同步和異步進行對比,假設(shè)有四個任務(wù)(編號為1,2,3,4),它們的執(zhí)行時間都是10ms,其中任務(wù)2是任務(wù)3的前置任務(wù),任務(wù)2需要20ms的響應(yīng)時間。下面我們做下對比,你就知道怎么實現(xiàn)的非阻塞I/O了。

適合的場景:

可以看出,當我們的程序需要大量I/O操作和用戶請求時,js這個具備單線程,異步,事件驅(qū)動多種氣質(zhì)的語言是多么應(yīng)景!相比于多線程語言,它不必耗費過多的系統(tǒng)開銷,同時也不必把精力用于處理多線程管理,相比于同步執(zhí)行的語言,宿主環(huán)境的異步和事件驅(qū)動機制又讓它實現(xiàn)了非阻塞I/O,所以你應(yīng)該知道它適合什么樣的場景了吧!

到此這篇關(guān)于JavaScript的單線程和異步詳細的文章就介紹到這了,更多相關(guān)JavaScript的單線程和異步內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論