javascript異步處理工作機(jī)制詳解
從基礎(chǔ)的層面來(lái)講,理解JavaScript的定時(shí)器是如何工作的是非常重要的。計(jì)時(shí)器的執(zhí)行常常和我們的直觀想象不同,那是因?yàn)镴avaScript引擎是單線(xiàn)程的。我們先來(lái)認(rèn)識(shí)一下下面三個(gè)函數(shù)是如何控制計(jì)時(shí)器的。
var id = setTimeout(fn, delay); - 初始化一個(gè)計(jì)時(shí)器,然后在指定的時(shí)間間隔后執(zhí)行。該函數(shù)返回一個(gè)唯一的標(biāo)志ID(Number類(lèi)型),我們可以使用它來(lái)取消計(jì)時(shí)器。
var id = setInterval(fn, delay); - 和setTimeout有些類(lèi)似,但它是連續(xù)調(diào)用一個(gè)函數(shù)(時(shí)間間隔是delay參數(shù))直到它被取消。
clearInterval(id);, clearTimeout(id); - 使用計(jì)時(shí)器ID(setTimeout 和 setInterval的返回值)來(lái)取消計(jì)時(shí)器回調(diào)的發(fā)生
為了理解計(jì)時(shí)器的內(nèi)在執(zhí)行原理,有一個(gè)重要的概念需要加以探討:計(jì)時(shí)器的延遲(delay)是無(wú)法得到保障的。由于所有JavaScript代碼是在一個(gè)線(xiàn)程里執(zhí)行的,所有異步事件(例如,鼠標(biāo)點(diǎn)擊和計(jì)時(shí)器)只有擁有執(zhí)行機(jī)會(huì)時(shí)才會(huì)執(zhí)行。
在這個(gè)圖表中有許多信息需要理解,如果完全理解了它們,你會(huì)對(duì)JavaScript引擎如何實(shí)現(xiàn)異步事件有一個(gè)很好的認(rèn)識(shí)。這是一個(gè)一維的圖標(biāo):垂 直方向表示時(shí)間,藍(lán)色的區(qū)塊表示JavaScript代碼執(zhí)行塊。例如第一個(gè)JavaScript代碼執(zhí)行塊需要大約18ms,鼠標(biāo)點(diǎn)擊所觸發(fā)的代碼執(zhí)行 塊需要11ms,等等。
由于JavaScript引擎同一時(shí)間只執(zhí)行一條代碼(這是由于JavaScript單線(xiàn)程的性質(zhì)),所以每一個(gè)JavaScript代碼執(zhí)行塊會(huì) “阻塞”其它異步事件的執(zhí)行。這就意味著當(dāng)一個(gè)異步事件發(fā)生(例如,鼠標(biāo)點(diǎn)擊,計(jì)時(shí)器被觸發(fā),或者Ajax異步請(qǐng)求)后,這些事件的回調(diào)函數(shù)將排在執(zhí)行隊(duì) 列的最后等待執(zhí)行(實(shí)際上,排隊(duì)的方式根據(jù)瀏覽器的不同而不同,所以這里只是一個(gè)簡(jiǎn)化);
從第一個(gè)JavaScript執(zhí)行塊開(kāi)始研究,在第一個(gè)執(zhí)行塊中兩個(gè)計(jì)時(shí)器被初始化:一個(gè)10ms的setTimeout()和一個(gè)10ms的setInterval()。 依據(jù)何時(shí)何地計(jì)時(shí)器被初始化(計(jì)時(shí)器初始化完畢后就會(huì)開(kāi)始計(jì)時(shí)),計(jì)時(shí)器實(shí)際上會(huì)在第一個(gè)代碼塊執(zhí)行完畢前被觸發(fā)。但是,計(jì)時(shí)器上綁定的函數(shù)不會(huì)立即執(zhí)行 (不被立即執(zhí)行的原因是JavaScript是單線(xiàn)程的)。實(shí)際上,被延遲的函數(shù)將依次排在執(zhí)行隊(duì)列的最后,等待下一次恰當(dāng)?shù)臅r(shí)間再執(zhí)行。
此外,在第一個(gè)JavaScript執(zhí)行塊中我們看到了一個(gè)“鼠標(biāo)點(diǎn)擊”事件發(fā)生了。一個(gè)JavaScript回調(diào)函數(shù)綁定在這個(gè)異步事件上了(我 們從來(lái)不知道用戶(hù)什么時(shí)候執(zhí)行這個(gè)(點(diǎn)擊)事件,因此認(rèn)為它是異步的),這個(gè)函數(shù)不會(huì)被立即執(zhí)行,和上面的計(jì)時(shí)器一樣,它將排在執(zhí)行隊(duì)列的最后,等待下一 次恰當(dāng)?shù)臅r(shí)候執(zhí)行。
當(dāng)?shù)谝粋€(gè)JavaScript執(zhí)行塊執(zhí)行完畢后,瀏覽器會(huì)立即問(wèn)一個(gè)問(wèn)題:哪個(gè)函數(shù)(語(yǔ)句)在等待被執(zhí)行?在這時(shí),一個(gè)“鼠標(biāo)點(diǎn)擊事件處理函數(shù)”和 一個(gè)“計(jì)時(shí)器回調(diào)函數(shù)”都在等待執(zhí)行。瀏覽器會(huì)選擇一個(gè)(實(shí)際上選擇了“鼠標(biāo)點(diǎn)擊事件的處理函數(shù)”,因?yàn)橛蓤D可知它是先進(jìn)隊(duì)的)立即執(zhí)行。而“計(jì)時(shí)器回調(diào) 函數(shù)”將等待下次適合的時(shí)間執(zhí)行。
注意,當(dāng)“鼠標(biāo)點(diǎn)擊事件處理函數(shù)”執(zhí)行的時(shí)候,setInterval的回調(diào)函數(shù)第一次被觸發(fā)了。和setTimeout的回調(diào)函數(shù)一樣,它將排到執(zhí)行隊(duì)列的最后等待執(zhí)行。但是,一定要注意這一點(diǎn):當(dāng)setInterval回調(diào)函數(shù)第二次被觸發(fā)時(shí)(此時(shí)setTimeout函數(shù)仍在執(zhí)行)setInterval的第一次觸發(fā)將被拋棄掉。當(dāng)一個(gè)很長(zhǎng)的代碼塊在執(zhí)行時(shí),可能把所有的setInterval回調(diào)函數(shù)都排在執(zhí)行隊(duì)列的后面,代碼塊執(zhí)行完之后,結(jié)果便會(huì)是一大串的setInterval回調(diào)函數(shù)等待執(zhí)行,并且這些函數(shù)之間沒(méi)有間隔,直到全部完成。所以,瀏覽器傾向于的當(dāng)沒(méi)有更多interval的處理函數(shù)在排隊(duì)時(shí)再將下一個(gè)處理函數(shù)排到隊(duì)尾(這是由于間隔的問(wèn)題)。
我們能夠發(fā)現(xiàn),當(dāng)?shù)谌齻€(gè)setInterval回調(diào)函數(shù)被觸發(fā)時(shí),之前的setInterval回調(diào)函數(shù)仍在執(zhí)行。這就說(shuō)明了一個(gè)很重要的事實(shí):setInterval不會(huì)考慮當(dāng)前正在執(zhí)行什么,而把所有的堵塞的函數(shù)排到隊(duì)列尾部。這意味著兩次setInterval回調(diào)函數(shù)之間的時(shí)間間隔會(huì)被犧牲掉(縮減)。
最后,當(dāng)?shù)诙€(gè)setInterval回調(diào)函數(shù)執(zhí)行完畢后,我們可以看到?jīng)]有任何程序等待JavaScript引擎執(zhí)行了。這就意味著瀏覽器現(xiàn)在在等待一個(gè)新的異步事件的發(fā)生。在50ms時(shí)一個(gè)新的setInterval回調(diào)函數(shù)再次被觸發(fā),這時(shí),沒(méi)有任何的執(zhí)行塊阻塞它的執(zhí)行了。所以它會(huì)立刻被執(zhí)行。
讓我們用一個(gè)例子來(lái)闡明setTimeout和setInterval之間的區(qū)別:
setTimeout ( function ( ) { /* Some long block of code... */ setTimeout (arguments. callee, 10 ); }, 10 ); setInterval ( function ( ) { /* Some long block of code... */ }, 10 );
這兩句代碼乍一看沒(méi)什么差別,但是它們是不同的。setTimeout回調(diào)函數(shù)的執(zhí)行和上一次執(zhí)行之間的間隔至少有10ms(可能會(huì)更多,但不會(huì)少于10ms),而setInterval的回調(diào)函數(shù)將嘗試每隔10ms執(zhí)行一次,不論上次是否執(zhí)行完畢。
在這里我們學(xué)到了很多知識(shí),總結(jié)一下:
JavaScript引擎是單線(xiàn)程的,強(qiáng)制所有的異步事件排隊(duì)等待執(zhí)行
setTimeout 和 setInterval 在執(zhí)行異步代碼的時(shí)候有著根本的不同
如果一個(gè)計(jì)時(shí)器被阻塞而不能立即執(zhí)行,它將延遲執(zhí)行直到下一次可能執(zhí)行的時(shí)間點(diǎn)才被執(zhí)行(比期望的時(shí)間間隔要長(zhǎng)些)
如果setInterval回調(diào)函數(shù)的執(zhí)行時(shí)間將足夠長(zhǎng)(比指定的時(shí)間間隔長(zhǎng)),它們將連續(xù)執(zhí)行并且彼此之間沒(méi)有時(shí)間間隔。
以上所述就是本文的全部?jī)?nèi)容了,希望能夠?qū)Υ蠹覍W(xué)習(xí)javascript異步處理有所幫助。
相關(guān)文章
原生js實(shí)現(xiàn)二級(jí)聯(lián)動(dòng)菜單
這篇文章主要為大家詳細(xì)介紹了原生js實(shí)現(xiàn)二級(jí)聯(lián)動(dòng)菜單,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11對(duì)于input 框限定輸入值為浮點(diǎn)型的js代碼
下面小編就為大家?guī)?lái)一篇對(duì)于input 框限定輸入值為浮點(diǎn)型的js代碼。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09JavaScript實(shí)現(xiàn)防止網(wǎng)頁(yè)被嵌入Frame框架的代碼分享
這篇文章主要介紹了JavaScript實(shí)現(xiàn)防止網(wǎng)頁(yè)被嵌入Frame框架的代碼分享,本文給出了2種防嵌入方法,需要的朋友可以參考下2014-12-12js 本地預(yù)覽的簡(jiǎn)單實(shí)現(xiàn)方法
本篇文章主要是對(duì)js本地預(yù)覽的簡(jiǎn)單實(shí)現(xiàn)方法進(jìn)行了介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2014-02-02JavaScript實(shí)現(xiàn)判斷圖片是否加載完成的3種方法整理
這篇文章主要介紹了JavaScript實(shí)現(xiàn)判斷圖片是否加載完成的3種方法整理,本文講解了onload方法、javascipt原生方法、jquery方法三種方法,需要的朋友可以參考下2015-03-03[分享]一個(gè)非常漂亮的進(jìn)度滾動(dòng)條
[分享]一個(gè)非常漂亮的進(jìn)度滾動(dòng)條...2007-02-02ExtJS實(shí)現(xiàn)文件下載的方法實(shí)例
這篇文章介紹了ExtJS實(shí)現(xiàn)文件下載的方法實(shí)例,有需要的朋友可以參考一下2013-11-11IE瀏覽器打印的頁(yè)眉頁(yè)腳設(shè)置解決方法
IE瀏覽器打印的頁(yè)眉頁(yè)腳設(shè)置解決方法2009-12-12bootstrap動(dòng)態(tài)調(diào)用select下拉框的實(shí)例代碼
今天小編就為大家分享一篇bootstrap動(dòng)態(tài)調(diào)用select下拉框的實(shí)例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08js之encodeURI、encodeURIComponent、decodeURI、decodeURIComponent
這篇文章主要介紹了js之encodeURI、encodeURIComponent、decodeURI、decodeURIComponent的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04