Javascript異步編程之setTimeout與setInterval詳解分析

一. setTimeout與setInterval詳細分析基本原理。

接下來這篇博客會總結setTimeout和setInterval基本點,對于上面三點會分三篇博客分別來總結,對于知道上面三點的人,但是又不是非常了解全面知識點的碼農(nóng)來說,沒有關系的,我們可以慢慢來學習,來理解,或者我總結不全面的或者不好地方可以留言,學習本來就是要互動,才有提高。當然對于那些知識大牛來說,也可以看下,如果我總結不好的話,也可以提提意見,我也可以多學習學習下!

在研究setTimeout與setInterval之前,我們可以先來看看一個小小的demo,其實總結與研究就是要多做demo,因為有的事情我們看起來很簡單,真正做起來的時候不是那么一回事。比如如下:

for(var i = 1; i <= 3; i++) {

setTimeout(function(){

console.log(i);

},100);

}```

如果javascript語言不是很熟悉的話,很多人會理所當然的認為for循環(huán)會分別打印出1,2,3. 但是事實不是這樣的,會輸出3次4. 要理解為什么會打印三次4,我們先來理解setTimeout這個函數(shù)吧,很多人會認為上面的setTimeout的意思是這樣的,在100毫秒后執(zhí)行setTimeout的回調函數(shù),其實這樣的理解是有誤的,其實setTimeout與setInterval真正的含義如下:

setTimeout:在指定的毫秒數(shù)后,將定時任務處理的函數(shù)添加到執(zhí)行隊列的隊尾。
setInterval:按照指定的周期(以毫秒數(shù)計時),將定時任務處理函數(shù)添加到執(zhí)行隊列的隊尾。
setTimeout與setInterval且都是異步的,所以我們現(xiàn)在可以來理解下上面循環(huán)為什么一直都是4呢?其實調用setTimeout時候,會有一個延時事件排入隊列,然后setTimeout調用之后的那行代碼運行,接著是再下一行代碼,直到再也沒有任何代碼了,javascript虛擬機才會問,隊列里還有嗎?如果隊列中至少有一個事件適合于觸發(fā),比如上面的setTimeout函數(shù),則會調用setTimeout那個函數(shù)。所以上面的代碼先for循環(huán),循環(huán)結束,而 i === 4一直遞增,直到不再滿足i<=3為止。所以就打印了3個4.

我們再來看看下面的函數(shù),如下:

setTimeout(function(){

console.log("打印我,我是異步執(zhí)行的");

},100);```

console.log("我是新來的,我要先執(zhí)行");

運行結果是:先打印出 “我是新來的,我先執(zhí)行”這句代碼,接著打印”打印我,我是異步執(zhí)行的”代碼。

二:理解javascript線程。

Javascript引擎是單線程運行的,瀏覽器無論在什么時候都只且只有一個線程在運行的。

那么單線程是如何配合瀏覽器內核處理這些定時器和相應瀏覽器事件呢?

瀏覽器內核允許多個線程異步執(zhí)行,這些線程在內核控制下相互配合以保持同步,比如一個瀏覽器至少有3個以上的線程,有:javascript引擎線程,界面渲染線程,瀏覽器事件觸發(fā)線程,除這些以外,也有一些執(zhí)行完的線程,比如http請求線程,這些異步線程都會產(chǎn)生不同的異步的事件。

界面渲染線程:

該線程負責渲染瀏覽器HTML界面元素,當界面需要重繪或由于某種操作引發(fā)回流(reflow),該線程就會執(zhí)行,該線程與javascript引擎線程是互斥的,因為javascript引擎運行腳本期間,瀏覽器渲染線程都是出于掛起狀態(tài)的,比如我們常見的是在頁面head標簽內不建議把JS放在頭部的原因,希望要把JS放在尾部或者使用異步加載等操作。因此在腳本中執(zhí)行對界面進行更新操作,如動態(tài)添加節(jié)點或者刪除節(jié)點等更新會把這些事件放在隊列當中,等javascript引擎空閑時才有機會渲染出來。

瀏覽器事件觸發(fā)線程:

用戶單擊一個已附加有單擊事件處理器dom元素時,會有一個單擊事件排入隊列,但是該單擊事件處理器要等到當前所有正在運行的代碼均已結束才會執(zhí)行。

比如如下一個小demo,我們平時寫代碼時候,特別用原審javascript寫tab切換的時候,經(jīng)常會碰到如下代碼,比如點擊一個li標簽,希望切換到對應的內容上來。如下點擊事件demo。我這里使用jquery來演示下:

HTML代碼如下結構:

< li class="container">點擊我1</ li >
< li class="container">點擊我2</ li >

< li class="container">點擊我3</ li >```

JS如下:

var lists = $(".container");

for(var i = 0, ilen = lists.length; i < ilen; i++) {

$(lists[i]).bind('click',function(){

console.log(i); // 打印3

});

}```

上面的代碼點擊一下,打印出3(不是0,1,2),原理還是和上面一樣。

定時觸發(fā)線程:

這里談到的定時計數(shù)器不是由javascript引擎計數(shù)的,因為javascript引擎是單線程的,如果處于堵塞狀態(tài)就計不了時的,它必須依賴外部計時并觸發(fā)定時,所以隊列中的定時事件也是異步事件。

三:理解setTimeout與setInterval異步事件:

Javascript最基礎的異步函數(shù)是setTimeout與setInterval,setTimeout會在一定的時間后執(zhí)行相應的函數(shù),它接受一個回調函數(shù)和一個毫秒時間,比如如下:

console.log( "a" );

setTimeout(function() {

console.log( "c" )

}, 500 );

setTimeout(function() {

console.log( "d" )

}, 500 );

setTimeout(function() {

console.log( "e" )

}, 500 );

console.log( "b" );```

控制臺先輸出“a”、“b”,大約500毫秒后,再看到“c”、“d”、“e”。

但是如果我把第一個setTimeout的延時時間改大一點或者改為600毫秒,那么打印出來就分別是a,b,d,e,c了。你可能聽過事件循環(huán)這個詞,它是用于描述隊列的工作方式的。當異步函數(shù)執(zhí)行時,回調函數(shù)就會被壓入這個隊列里面,,但是如果延遲時間不一樣的話,那么就不會了,就像上面的列子把定時毫秒數(shù)改大點輸出來的就不一樣了。

#四:異步函數(shù)的類型

在Javascript環(huán)境中提供的異步函數(shù)分為2大類:I/O函數(shù)和計時函數(shù)。

我們都知道創(chuàng)建nodeJS不是為了在服務器上運行javascript,而是因為javascript語言可以完美的實現(xiàn)非堵塞式的I/O。比如典型的ajax請求,如下代碼:

var url = "http://localhost/setTimeout/index2.php";

var xhr=new XMLHttpRequest;

xhr.open("GET","http://localhost/setTimeout/index2.php",true);

xhr.send();

xhr.onreadystatechange=function(){

if(xhr.readyState<4)return;

;

};

;


運行結果后先執(zhí)行”Ajax還沒完成呢?”,后執(zhí)行onreadystatechange的回調函數(shù)。在ajax函數(shù)中先執(zhí)行send方法后,再綁定事件呢,而不是先綁定事件,再send呢?

其實xhr對象使用了其他線程,這里涉及到一些跨線程通信的問題,跨線程訪問數(shù)據(jù)時需要使用委托,否則會發(fā)生數(shù)據(jù)沖突,所謂委托其實就是一個線程向另一個線程發(fā)送消息,但是xhr線程想要觸發(fā)主線程xhr對象的onreadystatechange事件就需要委托,而主線程目前是忙碌狀態(tài),它正在出理初始化消息,只有等到初始化消息空閑后才會執(zhí)行子線程的委托處理,而初始化消息空閑時就意味著onreadystatechange事件被綁定上了,所以后面的代碼執(zhí)行會永遠比xhr線程執(zhí)行要快。所以先會執(zhí)行后面的alert對話框,再執(zhí)行onreadystatechange事件。當然ajax請求第三個參數(shù)我們可以設置成false,同步請求,一般情況下還是異步請求好,但是為了處理一些特殊的需求,也可以設置同步請求(注意:同步請求會堵塞瀏覽器加載,所以如果請求的數(shù)據(jù)很大的時候,還是考慮異步請求。),比如一些常見的需求,發(fā)送ajax請求后,要打開一個新窗口這樣的一個需求,我們都知道如果是異步請求chrome和firefox直接會被攔截掉,但是如果我設置了同步請求就可以實現(xiàn)發(fā)送ajax請求后,再打開一個新窗口了。

我們已經(jīng)看到,異步函數(shù)非常適用于I/O操作,但是我們現(xiàn)在想讓一個函數(shù)在將來某時刻來運行或者一個動畫函數(shù)在將來某個時候來執(zhí)行動畫效果,這時候我們會想到javascript中的setTimeout與setInterval函數(shù)了。但是setTimeout與setInterval有如下缺陷:

當同一個javascript進程運行的代碼時候,任何javascript計時函數(shù)都無法使代碼運行起來,如下demo測試:

var start = new Date;

stTimeout(function(){

var end = new Date;

console.log("Time:",end-start,'ms');

},500);

while(new Date - start < 1000) {

}```

想打印出上面的console.log, 在瀏覽器一直刷新看到,第一次1020ms,第二次1029ms,反正結果一直是1s以上,也就是說后面的函數(shù)如果執(zhí)行時間非常長的話,那么setTimeout代碼永遠不會執(zhí)行。

  1. setInterval根據(jù)HTML規(guī)范可知:在一個小時之內會延遲 4-5ms這么一個延遲。也就是說使用這個計時不是非常精確。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容