關(guān)于js中的同步和異步

1.個(gè)人覺得沙廉,js中,最基礎(chǔ)的異步是setTimeout和setInterval函數(shù)臼节,很常見撬陵,但是很少人有人知道其實(shí)這就是異步,因?yàn)樗鼈兛梢钥刂苆s的執(zhí)行順序

<pre class="syntaxbox" style="margin: 0px; padding: 0px; white-space: pre-wrap; word-wrap: break-word; color: rgb(51, 51, 51); font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">var timeoutID = window.setTimeout(func[, delay, param1, param2, ...]);</pre>

看看下面的測試代碼會出現(xiàn)什么网缝?巨税? 要求出現(xiàn)的順序是張一

[
復(fù)制代碼

](javascript:void(0); "復(fù)制代碼")

<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;"> <script type="text/javascript"> console.log( "張一" );
setTimeout(function() {
console.log( "王三" )
}, 500 );
setTimeout(function() {
console.log( "李四" )
}, 500 );
setTimeout(function() {
console.log( "劉五" )
}, 500 );
console.log( "李二" ); </script></pre>

[
復(fù)制代碼

](javascript:void(0); "復(fù)制代碼")

結(jié)果正如設(shè)計(jì)的:

image

但是 查看 火狐的api文檔 有這樣一句話 api地址 https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout

Because even though setTimeout was called with a delay of zero, it's placed on a queue and scheduled to run at the next opportunity, not immediately. Currently executing code must complete before functions on the queue are executed, the resulting execution order may not be as expected.

翻譯一下 就是 因?yàn)榧词箂etTimeout調(diào)用延遲為零,這是放置在一個(gè)隊(duì)列和調(diào)度運(yùn)行到下一個(gè)機(jī)會途凫,沒有立即垢夹。當(dāng)前正在執(zhí)行的代碼必須在隊(duì)列上的函數(shù)執(zhí)行之前完成溢吻,由此產(chǎn)生的執(zhí)行順序可能不會像預(yù)期的那樣维费。

所以果元,可預(yù)料的結(jié)果是:直到在同一程序段中所有其余的代碼執(zhí)行結(jié)束后,超時(shí)才會發(fā)生犀盟。所以如果設(shè)置了超時(shí)而晒,同時(shí)執(zhí)行了需長時(shí)間運(yùn)行的函數(shù),那么在該函數(shù)執(zhí)行完成之前阅畴,超時(shí)甚至都不會啟動倡怎。實(shí)際上,異步函數(shù)贱枣,如setTimeout和setInterval监署,被壓入了稱之為Event Loop的隊(duì)列。有點(diǎn)扯遠(yuǎn)纽哥,記住就是setTimeout和setInterval可以改變一個(gè)隊(duì)列函數(shù)的執(zhí)行順序钠乏。

2.ajax異步

很多時(shí)候,我們使用的ajax都是jQuery封裝后的.ajax() 如:api中的ajax格式春塌。

[
復(fù)制代碼

](javascript:void(0); "復(fù)制代碼")

<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;"> $.ajax({
type: "POST",
url: "some.php",
data: "name=John&location=Boston",
success: function(msg){
alert( "Data Saved: " + msg );
}
});</pre>

[
復(fù)制代碼

](javascript:void(0); "復(fù)制代碼")

很多人的理解是 是在調(diào)用$.ajax之后馬上使用data晓避? 但是真的是這樣嗎?

如果你寫過原生的ajax只壳,應(yīng)該會知道如下:https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Types_of_requests

[
復(fù)制代碼

](javascript:void(0); "復(fù)制代碼")

<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">function reqListener () {
console.log(this.responseText);
} var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "http://www.example.org/example.txt");
oReq.send();</pre>

[
復(fù)制代碼

](javascript:void(0); "復(fù)制代碼")

這個(gè)火狐官方的例子很好的說明我們的ajax執(zhí)行的步驟俏拱,XmlHttpRequest對象發(fā)起請求,設(shè)置回調(diào)函數(shù)用來處理XHR的readystatechnage事件吼句。然后執(zhí)行XHR的send方法锅必。在XHR運(yùn)行中,當(dāng)其屬性readyState改變時(shí)readystatechange事件就會被觸發(fā)惕艳,只有在XHR從遠(yuǎn)端服務(wù)器接收響應(yīng)結(jié)束時(shí)回調(diào)函數(shù)才會觸發(fā)執(zhí)行况毅。也就是如下:

[
復(fù)制代碼

](javascript:void(0); "復(fù)制代碼")

<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">xmlhttp.open( "GET", "url", true );
xmlhttp.onreadystatechange = function( data ) { if ( xmlhttp.readyState === 4 ) {
console.log( data );
}
};
xmlhttp.send( null );</pre>

[
復(fù)制代碼

](javascript:void(0); "復(fù)制代碼")

以上兩點(diǎn)就是JavaScript中的同步和異步。

延伸 摘自阮一峰e(cuò)s6入門:

于是尔艇,這個(gè)問題又回到了最開始的起點(diǎn):JavaScript是單線程的尔许。

JavaScript語言的一大特點(diǎn)就是單線程,也就是說终娃,同一個(gè)時(shí)間只能做一件事味廊。那么,為什么JavaScript不能有多個(gè)線程呢棠耕?這樣能提高效率啊余佛。

JavaScript的單線程,與它的用途有關(guān)窍荧。作為瀏覽器腳本語言辉巡,JavaScript的主要用途是與用戶互動,以及操作DOM蕊退。這決定了它只能是單線程郊楣,否則會帶來很復(fù)雜的同步問題憔恳。比如,假定JavaScript同時(shí)有兩個(gè)線程净蚤,一個(gè)線程在某個(gè)DOM節(jié)點(diǎn)上添加內(nèi)容钥组,另一個(gè)線程刪除了這個(gè)節(jié)點(diǎn),這時(shí)瀏覽器應(yīng)該以哪個(gè)線程為準(zhǔn)今瀑?

所以程梦,為了避免復(fù)雜性,從一誕生橘荠,JavaScript就是單線程屿附,這已經(jīng)成了這門語言的核心特征,將來也不會改變哥童。

為了利用多核CPU的計(jì)算能力拿撩,HTML5提出Web Worker標(biāo)準(zhǔn),允許JavaScript腳本創(chuàng)建多個(gè)線程如蚜,但是子線程完全受主線程控制压恒,且不得操作DOM。所以错邦,這個(gè)新標(biāo)準(zhǔn)并沒有改變JavaScript單線程的本質(zhì)探赫。

單線程就意味著,所有任務(wù)需要排隊(duì)撬呢,前一個(gè)任務(wù)結(jié)束伦吠,才會執(zhí)行后一個(gè)任務(wù)。如果前一個(gè)任務(wù)耗時(shí)很長魂拦,后一個(gè)任務(wù)就不得不一直等著毛仪。于是就有一個(gè)概念,任務(wù)隊(duì)列芯勘。

如果排隊(duì)是因?yàn)橛?jì)算量大箱靴,CPU忙不過來,倒也算了荷愕,但是很多時(shí)候CPU是閑著的衡怀,因?yàn)镮O設(shè)備(輸入輸出設(shè)備)很慢(比如Ajax操作從網(wǎng)絡(luò)讀取數(shù)據(jù)),不得不等著結(jié)果出來安疗,再往下執(zhí)行抛杨。

JavaScript語言的設(shè)計(jì)者意識到,這時(shí)主線程完全可以不管IO設(shè)備荐类,掛起處于等待中的任務(wù)怖现,先運(yùn)行排在后面的任務(wù)。等到IO設(shè)備返回了結(jié)果玉罐,再回過頭屈嗤,把掛起的任務(wù)繼續(xù)執(zhí)行下去潘拨。

于是,所有任務(wù)可以分成兩種恢共,一種是同步任務(wù)(synchronous),另一種是異步任務(wù)(asynchronous)璧亚。同步任務(wù)指的是讨韭,在主線程上排隊(duì)執(zhí)行的任務(wù),只有前一個(gè)任務(wù)執(zhí)行完畢癣蟋,才能執(zhí)行后一個(gè)任務(wù)透硝;異步任務(wù)指的是,不進(jìn)入主線程疯搅、而進(jìn)入"任務(wù)隊(duì)列"(task queue)的任務(wù)濒生,只有"任務(wù)隊(duì)列"通知主線程甜橱,某個(gè)異步任務(wù)可以執(zhí)行了润绎,該任務(wù)才會進(jìn)入主線程執(zhí)行。

具體來說蜕衡,異步執(zhí)行的運(yùn)行機(jī)制如下礁蔗。(同步執(zhí)行也是如此觉义,因?yàn)樗梢员灰暈闆]有異步任務(wù)的異步執(zhí)行。)

(1)所有同步任務(wù)都在主線程上執(zhí)行浴井,形成一個(gè)執(zhí)行棧(execution context stack)晒骇。

(2)主線程之外,還存在一個(gè)"任務(wù)隊(duì)列"(task queue)磺浙。只要異步任務(wù)有了運(yùn)行結(jié)果洪囤,就在"任務(wù)隊(duì)列"之中放置一個(gè)事件。

(3)一旦"執(zhí)行棧"中的所有同步任務(wù)執(zhí)行完畢撕氧,系統(tǒng)就會讀取"任務(wù)隊(duì)列"瘤缩,看看里面有哪些事件。那些對應(yīng)的異步任務(wù)伦泥,于是結(jié)束等待狀態(tài)款咖,進(jìn)入執(zhí)行棧,開始執(zhí)行奄喂。

(4)主線程不斷重復(fù)上面的第三步铐殃。

下圖就是主線程和任務(wù)隊(duì)列的示意圖。

任務(wù)隊(duì)列

只要主線程空了跨新,就會去讀取"任務(wù)隊(duì)列"富腊,這就是JavaScript的運(yùn)行機(jī)制。這個(gè)過程會不斷重復(fù)域帐。

"任務(wù)隊(duì)列"是一個(gè)事件的隊(duì)列(也可以理解成消息的隊(duì)列)赘被,IO設(shè)備完成一項(xiàng)任務(wù)是整,就在"任務(wù)隊(duì)列"中添加一個(gè)事件,表示相關(guān)的異步任務(wù)可以進(jìn)入"執(zhí)行棧"了民假。主線程讀取"任務(wù)隊(duì)列"浮入,就是讀取里面有哪些事件。

"任務(wù)隊(duì)列"中的事件羊异,除了IO設(shè)備的事件以外事秀,還包括一些用戶產(chǎn)生的事件(比如鼠標(biāo)點(diǎn)擊、頁面滾動等等)野舶。只要指定過回調(diào)函數(shù)易迹,這些事件發(fā)生時(shí)就會進(jìn)入"任務(wù)隊(duì)列",等待主線程讀取平道。

所謂"回調(diào)函數(shù)"(callback)睹欲,就是那些會被主線程掛起來的代碼。異步任務(wù)必須指定回調(diào)函數(shù)一屋,當(dāng)主線程開始執(zhí)行異步任務(wù)窘疮,就是執(zhí)行對應(yīng)的回調(diào)函數(shù)。

"任務(wù)隊(duì)列"是一個(gè)先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)冀墨,排在前面的事件考余,優(yōu)先被主線程讀取。主線程的讀取過程基本上是自動的轧苫,只要執(zhí)行棧一清空楚堤,"任務(wù)隊(duì)列"上第一位的事件就自動進(jìn)入主線程。但是含懊,由于存在后文提到的"定時(shí)器"功能身冬,主線程首先要檢查一下執(zhí)行時(shí)間,某些事件只有到了規(guī)定的時(shí)間岔乔,才能返回主線程酥筝。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市雏门,隨后出現(xiàn)的幾起案子嘿歌,更是在濱河造成了極大的恐慌,老刑警劉巖茁影,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宙帝,死亡現(xiàn)場離奇詭異,居然都是意外死亡募闲,警方通過查閱死者的電腦和手機(jī)步脓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人靴患,你說我怎么就攤上這事仍侥。” “怎么了鸳君?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵农渊,是天一觀的道長。 經(jīng)常有香客問我或颊,道長砸紊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任饭宾,我火速辦了婚禮批糟,結(jié)果婚禮上格了,老公的妹妹穿的比我還像新娘看铆。我一直安慰自己,他們只是感情好盛末,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布弹惦。 她就那樣靜靜地躺著,像睡著了一般悄但。 火紅的嫁衣襯著肌膚如雪棠隐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天檐嚣,我揣著相機(jī)與錄音助泽,去河邊找鬼。 笑死嚎京,一個(gè)胖子當(dāng)著我的面吹牛嗡贺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鞍帝,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼诫睬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了帕涌?” 一聲冷哼從身側(cè)響起摄凡,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蚓曼,沒想到半個(gè)月后亲澡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡纫版,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年谷扣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡会涎,死狀恐怖裹匙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情末秃,我是刑警寧澤概页,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站练慕,受9級特大地震影響惰匙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜铃将,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一项鬼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧劲阎,春花似錦绘盟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锡垄,卻和暖如春沦零,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背货岭。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工路操, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人千贯。 一個(gè)月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓屯仗,卻偏偏與公主長得像,于是被迫代替她去往敵國和親丈牢。 傳聞我的和親對象是個(gè)殘疾皇子祭钉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容