JavaScript的異步

前言


????在Javascript這樣類(lèi)型的語(yǔ)言中編程最重要但最常被人誤解的部分之一,就是如何控制在一段時(shí)間內(nèi)程序的行為次序.同時(shí),JavaScript中的異步,也經(jīng)常被人和并行搞混.今天,我們來(lái)談一下JavaScript中的異步.

JS開(kāi)始以來(lái),異步編程一直存在.然而,但是大多數(shù)JS開(kāi)發(fā)人員從未真正仔細(xì)考慮過(guò)在程序中如何以及為何出現(xiàn)問(wèn)題,也沒(méi)有去探索各種其他處理方法。 比較好的方法一直是稀里糊涂的使用回調(diào)函數(shù).到今天為止,許多人會(huì)堅(jiān)持認(rèn)為回調(diào)使用起來(lái)就已經(jīng)綽綽有余了.

什么是異步?

????首先,一段JavaScript程序是由多個(gè)塊(chunk)組成的,最常見(jiàn)的塊就是function–函數(shù).

我們把一段時(shí)間內(nèi),程序要執(zhí)行的任務(wù)分為兩部分:?

1.執(zhí)行部分(現(xiàn)在執(zhí)行的),2.等待部分(剩下的將來(lái)要執(zhí)行的).而我們面臨的問(wèn)題是,當(dāng)現(xiàn)在執(zhí)行部分執(zhí)行完后,程序并不是嚴(yán)格地立馬去完成等待部分.換句話(huà)就是,這些塊是異步執(zhí)行的.我們不會(huì)像預(yù)期的那樣阻塞地完成一個(gè)接一個(gè)的任務(wù).

例如:

//ajax是某些JavaScript框架(如:jQurey)中實(shí)現(xiàn)Ajax的函數(shù)

let data =ajax( "http://some.url.1" );

//控制臺(tái)輸出data內(nèi)容

console.log(data)

如果運(yùn)行這段JavaScript代碼會(huì)發(fā)現(xiàn),打印出來(lái)的data通常沒(méi)有我們想要的ajax請(qǐng)求結(jié)果.

這是因?yàn)?/p>

,Ajax請(qǐng)求并不是同步(synchronously,相對(duì)于異步asynchronously)完成的,當(dāng)執(zhí)行console.log()的時(shí)候,我們想要的data還沒(méi)有返回.我們想要的其實(shí)是ajax(...)函數(shù)能夠阻塞,一直到請(qǐng)求結(jié)果返回,最簡(jiǎn)單的解決方法就是回調(diào)(callback).

//回調(diào)方式的一個(gè)示例,具體回調(diào)方式根據(jù)具體來(lái)定.

ajax( "http://some.url.1",functionmyCallbackFunction(data){

console.log( data );

} );

這時(shí)我們會(huì)發(fā)現(xiàn),data就是我們想要的了.

注意:我們是可以同步地請(qǐng)求

Ajax的,比如:jQurey中的ajax()將async: false加入設(shè)置.但是這樣做的后果就是瀏覽器的UI操作(按鈕,滾動(dòng)等)以及用戶(hù)交互等都會(huì)被阻塞等待鎖死.我們應(yīng)該避免這種情況,一團(tuán)亂麻的回調(diào)函數(shù)也不應(yīng)成為使用同步Ajax的理由.

記下來(lái)我們?cè)倏紤]另一個(gè)例子幫助理解:

functionnow() {

return21;

}

function later() {

answer=answer* 2;

console.log("answer:",answer);

}

varanswer= now();

setTimeout(later, 1000 ); // answer: 42

我們?cè)儆脛偛诺乃悸啡ダ斫膺@個(gè)程序:分為兩個(gè)部分:執(zhí)行部分,等待部分.

執(zhí)行部分是:

//回調(diào)方式的一個(gè)示例,具體回調(diào)方式根據(jù)具體來(lái)定.

ajax( "http://some.url.1",functionmyCallbackFunction(data){

console.log( data );

} );

等待部分就是later()中的內(nèi)容:

answer=answer* 2;

console.log( "answer:",answer);

執(zhí)行部分會(huì)立刻執(zhí)行,而setTimeout(...)會(huì)設(shè)定一個(gè)事件(timeout事件),在1000ms后執(zhí)行l(wèi)ater().就像這樣,每當(dāng)我們?cè)趂unction中寫(xiě)一段代碼,并讓它在事件(timer,鼠標(biāo)事件,Ajax響應(yīng)等)響應(yīng)后執(zhí)行,我們就創(chuàng)造了一個(gè)等待部分,也就是在程序中使用了異步.

Event Loop

????雖然我們?cè)谶@里談異步,但是,直到ES6*,JavaScript本身并沒(méi)有內(nèi)置異步的概念.聽(tīng)起來(lái)很震驚,但事實(shí)確實(shí)是這樣的.我們會(huì)問(wèn):那我們討論的異步是怎么實(shí)現(xiàn)的呢??

我們都知道的是

JavaScript引擎從來(lái)不是獨(dú)立執(zhí)行,總要依賴(lài)于一個(gè)環(huán)境,比如,我們最熟悉的web瀏覽器.以及服務(wù)器上的Node.js.這些環(huán)境會(huì)用一個(gè)機(jī)制來(lái)隨時(shí)間使用JavaScript引擎處理我們的多個(gè)程序塊,這個(gè)機(jī)制我們管它叫Event Loop.

換句話(huà)說(shuō),

JavaScript引擎并不知道什么時(shí)候執(zhí)行,而是被執(zhí)行環(huán)境的線(xiàn)程來(lái)安排處理哪些程序塊,執(zhí)行環(huán)境根據(jù)事件來(lái)調(diào)度JavaScript引擎處理.

那么什么是

Event Loop呢?

我們通過(guò)一段偽代碼來(lái)了解它的概念:

//eventLoop是事件排成的先進(jìn)先出的隊(duì)列(queue)

vareventLoop= [ ];

var event;

while(true) {

// 處理完一個(gè)事件

if (eventLoop.length > 0) {

// 獲取隊(duì)列中的下個(gè)動(dòng)作

event=eventLoop.shift();

// 處理剛才取出的動(dòng)作

try {

event();

}

catch (err) {

reportError(err);

}

}

}

我們通過(guò)這段偽代碼大體了解它的機(jī)制.我們有一個(gè)循環(huán),循環(huán)的每一個(gè)迭代中,如果在等待隊(duì)列中存在事件,就會(huì)被取出并處理,event()就是各種回調(diào)函數(shù).

因此,到這兒我們就可以明白了,

setTimeout(..)不是把設(shè)定好的回調(diào)函數(shù)安排到event loop中,而是將一個(gè)計(jì)時(shí)器(timer)安排在event loop中,當(dāng)計(jì)時(shí)器到期,執(zhí)行環(huán)境將回調(diào)推入event loop,這樣,在將來(lái)某個(gè)時(shí)間會(huì)被取出并執(zhí)行.

假如,現(xiàn)在

event loop中已經(jīng)存在20個(gè)等待的成員,那么這個(gè)回調(diào)就應(yīng)該等待,通常沒(méi)有方法能將他移動(dòng)到隊(duì)列頭部,讓他立馬執(zhí)行.這樣就產(chǎn)生了,哪怕用了setTimeout(..),指定的回調(diào)并不會(huì)在指定時(shí)間后立即執(zhí)行的現(xiàn)象,當(dāng)然也不會(huì)提前,至于是否要等待,等待多久,要根據(jù)具體情況來(lái)說(shuō).

注意:之所以說(shuō)是”直到ES6”,是因?yàn)镋S6引入了Promise機(jī)制,ES6通過(guò)Promise將event loop的工作機(jī)制納入到了JavaScript引擎的工作范圍,而不只是執(zhí)行環(huán)境的工作.關(guān)于Promise以后有機(jī)會(huì)再談.

并行

有一個(gè)常見(jiàn)的現(xiàn)象就是,人們經(jīng)常把”異步”和”并行”混為一談,其實(shí)他們大不相同.”異步”,指的是執(zhí)行部分和等待部分中間有時(shí)間差,并不是立即執(zhí)行.而并行則是指一起執(zhí)行.

并行計(jì)算中最常見(jiàn)的單位是

進(jìn)程(process)和線(xiàn)程(thread),進(jìn)程和線(xiàn)程之間可以是獨(dú)立執(zhí)行,也可以在一個(gè)處理器中,或者一臺(tái)電腦中同時(shí)執(zhí)行.通常,多個(gè)線(xiàn)程可以共享單個(gè)進(jìn)程的內(nèi)存.

相比之下

,event loop是將一個(gè)工作分解成多個(gè)任務(wù),并組成隊(duì)列串行執(zhí)行,不能并行訪(fǎng)問(wèn)和更改共享的內(nèi)存.它的并行性和”串行性”可以在不同線(xiàn)程下的event loop上體現(xiàn)(一個(gè)線(xiàn)程可以創(chuàng)立一個(gè)event loop,不同線(xiàn)程下的event loop具有并行性,單個(gè)event loop具有串行性).

并行地

執(zhí)行線(xiàn)程和異步地交錯(cuò)處理事件在粒度級(jí)別上有著很大的不同.線(xiàn)程是表達(dá)式操作級(jí)別,而異步是函數(shù)級(jí)別。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容