摘要:一個(gè)頁(yè)面在瀏覽器顯示出來(lái)至少需要個(gè)線程,分別是引擎,渲染,事件觸發(fā)。其中事件觸發(fā)是獨(dú)立于其他個(gè)執(zhí)行的,而和是相互排斥的,也就是說(shuō)同一個(gè)時(shí)間二者只有一個(gè)在工作。
作為DOM本身十分重要的2個(gè)異步執(zhí)行函數(shù),初學(xué)者感覺(jué)這個(gè)很不好理解,我簡(jiǎn)單寫一寫我的理解
setTimeout (func, millisec); setInterval(func, millisec);
這兩個(gè)方法在形式看起來(lái)很相似,第一個(gè)參數(shù)是異步執(zhí)行的函數(shù)(用字符串表示的代碼也可以,不過(guò)很少這樣用),第二個(gè)參數(shù)是時(shí)間(ms)。但其實(shí)這兩個(gè)函數(shù)還是有很大區(qū)別的
說(shuō)的通俗一點(diǎn),setTimeout()讓電腦一定時(shí)間以后再執(zhí)行一段代碼,執(zhí)行完了就拉倒,例如下面這段代碼:
setTimeout("alert("5 seconds!")", 5000); //5秒后彈出提示框
再看看setInterval(),不同的在于每過(guò)一段時(shí)間會(huì)重復(fù)執(zhí)行一段代碼。比如在瀏覽器中輸出一個(gè)時(shí)間,每一秒變化一下。
var clk = document.getElementById("clk"); //clk是一個(gè)div setInterval(function(){ var t = new Date(); clk.innerHTML= t; }, 200); //每隔200ms顯示一次時(shí)間,200ms會(huì)看著比1000ms更穩(wěn)定一些
運(yùn)行上面這個(gè)代碼,如果你得到的時(shí)間不準(zhǔn)確,那一定是你的電腦時(shí)間錯(cuò)了。
好了,總結(jié)一下:
setTimeout(func, m); //m毫秒后執(zhí)行函數(shù)func,只執(zhí)行一次(下文會(huì)講怎么讓它中止) setInterval(func, m); //每隔m毫秒執(zhí)行函數(shù)func,一直執(zhí)行下去(下文會(huì)講怎么讓它停下來(lái))
對(duì)于setInterval()的用途可能還比較好理解,但是對(duì)于setTimeout(),這里有一個(gè)問(wèn)題——我們?yōu)槭裁匆屢欢未a過(guò)一段時(shí)間在執(zhí)行?
這里就產(chǎn)生了一個(gè)很重要的概念,在本文一開(kāi)始就提到過(guò)——異步!什么是異步?
舉個(gè)通俗一點(diǎn)的例子:比如現(xiàn)在有燒水和擦桌子兩件事,如果你一定要用熱水擦桌子,那你就必須先等著水開(kāi)了,才能擦桌子,這個(gè)就是同步;如果你不在乎用什么水擦桌子,那一般人都會(huì)先燒水,在燒水的同時(shí)就開(kāi)始擦桌子了,這個(gè)就是異步。
對(duì)于計(jì)算機(jī)而言,這個(gè)邏輯和正常生活不一樣:同時(shí)執(zhí)行就是異步,先后執(zhí)行就是同步!當(dāng)然這是一種簡(jiǎn)單的理解,并不嚴(yán)謹(jǐn),畢竟在計(jì)算機(jī)內(nèi)部計(jì)時(shí)器脈沖、操作系統(tǒng)進(jìn)程、網(wǎng)絡(luò)通信中都有同步和異步的概念。
有了異步執(zhí)行這個(gè)概念,那小編可以負(fù)責(zé)的說(shuō)setTimeout()和setInterval()都是異步執(zhí)行的。其實(shí)2015年出的ES6在異步這里下了很大的功夫,提出了async function(){}、Promise對(duì)象、Generator函數(shù)、Object.observe函數(shù)等很多新概念,不過(guò)這里不談這些概念,但理解這些異步概念絕不能像這篇文章里面簡(jiǎn)單粗暴!!!
好了現(xiàn)在可以回答上面那個(gè)問(wèn)題了,我們之所以會(huì)用到setTimeout()和setInterval()更多還是為了異步執(zhí)行代碼,以此提高代碼的執(zhí)行速度。到此這兩個(gè)方法的就講完了。。。等等!!!方法?方法會(huì)不會(huì)有返回值呀!
沒(méi)錯(cuò),這兩個(gè)函數(shù)都有返回值,至于這個(gè)值是個(gè)什么并不重要,我們只需要知道這個(gè)值能干什么,我們給上面的那段代碼添點(diǎn)東西:
var clk = document.getElementById("clk"); //clk是一個(gè)div var time = setInterval(function(){ var t = new Date() clk.innerHTML= t; }, 200); var btn = document.getElementById("btn"); //btn是一個(gè)按鈕 btn.onclick = function(){ clearInterval(time); };
這段代碼我們獲得了setInterval() 的返回值,把返回值傳給了clearInterval()方法,這樣實(shí)現(xiàn)了點(diǎn)擊按鈕結(jié)束的對(duì)應(yīng)setInterval()的反復(fù)調(diào)用,終于它可以停下來(lái)了。
同樣的方法,利用clearTimeout() 方法可取消由 setTimeout() 函數(shù)定義的異步操作,我們?nèi)绶ㄅ谥频玫揭粋€(gè)取消延遲事件的按鈕(當(dāng)然,如果這個(gè)事件已經(jīng)執(zhí)行了再點(diǎn)這個(gè)按鈕就沒(méi)意義了):
var alt = setTimeout("alert("5 seconds!")", 5000); //5秒后彈出提示框 var btn = document.getElementById("btn"); //btn是一個(gè)按鈕 btn.onclick = function(){ clearTimeout(alt); };
如果用setInterval和setTimeout調(diào)用的函數(shù)是一個(gè)有參數(shù)的函數(shù)怎么辦?
function f(a, b){/*...*/} var num = 2; setTimeout("f(num, 3)", 1000); //第一種方法 setTimeout(function(){fun(num, 3);}, 1000); //第二種方法
這一下,setTimeout()和setInterval()已經(jīng)理解了,看它們相似點(diǎn)其實(shí)也不少,否則怎么會(huì)容易被搞混呢?其實(shí)我們可以使用setTimeout()實(shí)現(xiàn)setInterval()的功能,就像下面一段代碼:
function mySetInterval(code, ms){ if(typeof code === "string") //不要忘了第一個(gè)參數(shù)可以是字符串 eval(code); else if(typeof code === "function") code(); else throw new Error("code cannot be run"); //當(dāng)?shù)谝粋€(gè)參數(shù)傳入不是字符串或函數(shù)時(shí)報(bào)錯(cuò) setTimeout(function(){ mySetInterval(code, ms); //遞歸調(diào)用 },ms); } mySetInterval("document.write("helloWorld!
")", 1000);//調(diào)用方法不變,第一個(gè)參數(shù)也可以是函數(shù)
這里值得強(qiáng)調(diào)的是eval()并不是什么好東西,它在全局運(yùn)行,對(duì)字符串并不能有效檢查(會(huì)讓JSLint失效),還會(huì)調(diào)用編譯器降低效率,同時(shí)帶來(lái)安全隱患。所以盡量不要用eval(),也不要給setTimeout和setInterval傳入字符串,因?yàn)橄到y(tǒng)也是用eval實(shí)現(xiàn)這個(gè)功能的。
這段代碼只是用這樣一種方式表示setTimeout()和setInterval()的關(guān)系,便于讀者理解。并不表示這樣做有什么好,更不表示編譯器也這么實(shí)現(xiàn)setInterval(),因?yàn)檫@樣的遞歸效率不高占用資源卻不少,而且它沒(méi)法停止。
如果你已經(jīng)掌握了上面的內(nèi)容,那么下面可以更深入的理解一下異步了。
以setTimeout為例:
//alert(1); //setTimeout("alert(2)", 0); //alert(3); //alert(3);
上面這段代碼會(huì)如何輸出呢?實(shí)際輸出是:1 -> 3 -> 3 ->2,為什么會(huì)這樣,難道setTimeout(func, 0)不是立即執(zhí)行?沒(méi)錯(cuò)。想理解這個(gè)問(wèn)題,必須簡(jiǎn)單理解瀏覽器是如何處理異步函數(shù)的。
一個(gè)頁(yè)面在瀏覽器顯示出來(lái)至少需要3個(gè)線程,分別是js引擎,GUI渲染,事件觸發(fā)。其中事件觸發(fā)是獨(dú)立于其他2個(gè)執(zhí)行的,而js和GUI是相互排斥的,也就是說(shuō)同一個(gè)時(shí)間二者只有一個(gè)在工作。好了,這說(shuō)明js引擎是單線程執(zhí)行的,當(dāng)?shù)诙械?b>setTimeout執(zhí)行以后,js引擎把func(第一個(gè)參數(shù))放入異步隊(duì)列(瀏覽器再開(kāi)一個(gè)線程),然后繼續(xù)向下執(zhí)行,此后,當(dāng)js引擎空閑下來(lái)才會(huì)把異步執(zhí)行的結(jié)果插入原來(lái)js線程中。是不是這樣呢,我們讓代碼說(shuō)話:
var finish = true; setTimeout(function(){ finish = false; //1s后,改變isEnd的值 }, 0); while(finish); alert("finished"); //永遠(yuǎn)不會(huì)執(zhí)行
上方這段代碼是個(gè)死循環(huán),就因?yàn)閖s引擎不能空閑下來(lái),異步函數(shù)也就沒(méi)有執(zhí)行。下面這個(gè)實(shí)際問(wèn)題可以很好的理解這個(gè):
click "first" button to calculate.
click "second" button to calculate.
我們用了一很大的循環(huán)模擬一個(gè)耗費(fèi)時(shí)間的計(jì)算。
分析一下:第一個(gè)按鈕看不到calculating。由于js引擎的事件處理也是異步的,而for循環(huán)是同步的,設(shè)置文字為calculating的語(yǔ)句被放在了for循環(huán)結(jié)束,因?yàn)橹挥写藭r(shí)js才有空閑處理異步隊(duì)列,for結(jié)束了以后,文字被設(shè)置為calculating,繼而變?yōu)镈one,所以我們看不到這個(gè)過(guò)程了,在代碼中debugger的位置停一下,這個(gè)過(guò)程就清晰的呈現(xiàn)了出來(lái)。
為了解決這個(gè)問(wèn)題,第二個(gè)按鈕引入了setTimeout這樣,異步事件click執(zhí)行,函數(shù)內(nèi)第一個(gè)語(yǔ)句被送入隊(duì)列,而后setTimeout里那個(gè)匿名函數(shù)被送入隊(duì)列,此時(shí)js引擎有空閑,于是輸出calculating,異步隊(duì)列移動(dòng),繼續(xù)執(zhí)行calc,這樣就是我們想看到的結(jié)果了。
不足之處請(qǐng)多指點(diǎn)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/97594.html
摘要:注意如果主邏輯的代碼執(zhí)行時(shí)間已經(jīng)超過(guò)了第二個(gè)參數(shù)設(shè)置的時(shí)間,那么等運(yùn)行到該回調(diào)函數(shù)時(shí),它會(huì)忽略掉這個(gè)時(shí)間,并立即執(zhí)行。如果某一個(gè)進(jìn)行大量的計(jì)算,那么它就會(huì)阻塞在當(dāng)前的回調(diào)函數(shù)中,等待該計(jì)算完成后,再執(zhí)行下一個(gè)的回調(diào)函數(shù)。 setTimeout() ? JavaScript是一個(gè)單線程的語(yǔ)言,也就是說(shuō)它同一時(shí)間只能執(zhí)行一段代碼,接下來(lái)我們通過(guò)兩個(gè)例子說(shuō)明一下單線程語(yǔ)言和多線程語(yǔ)言的...
摘要:不過(guò)兩者各有各的應(yīng)用場(chǎng)景。方法實(shí)際上,和的語(yǔ)法相同。這意味著如果函數(shù)的主體部分需要秒鐘執(zhí)行完,那么整個(gè)函數(shù)則要每秒鐘才執(zhí)行一次。不過(guò)還是有辦法可以終止和函數(shù)的執(zhí)行。 var intervalProcess = setInterval(alert(GOAL!), 3000); 這個(gè)變量命名可以在實(shí)際代碼中采用 這兩個(gè)方法都可以用來(lái)實(shí)現(xiàn)在一個(gè)固定時(shí)間段之后去執(zhí)行JavaScript。不過(guò)...
摘要:設(shè)置和清除定時(shí)器直接引用忍者秘籍中的圖片注意定時(shí)器的時(shí)間間隔設(shè)為,也會(huì)有幾毫秒的延遲。以上參考資料忍者秘籍第章馴服線程和定時(shí)器 showImg(https://segmentfault.com/img/remote/1460000015353524?w=1024&h=681); 前言 前段時(shí)間剛看完《JS忍者秘籍》,雖說(shuō)是15年出版的,有些東西是過(guò)時(shí)了,但像對(duì)原型鏈、閉包、正則、定時(shí)器...
摘要:提供定時(shí)執(zhí)行代碼的功能,叫做定時(shí)器,主要由和這兩個(gè)函數(shù)來(lái)完成。它返回一個(gè)整數(shù),表示定時(shí)器的編號(hào),以后可以用來(lái)取消這個(gè)定時(shí)器。上面代碼中,回調(diào)函數(shù)不會(huì)再執(zhí)行了,因?yàn)閮蓚€(gè)定時(shí)器都被取消了。 JavaScript 提供定時(shí)執(zhí)行代碼的功能,叫做定時(shí)器(timer),主要由setTimeout()和setInterval()這兩個(gè)函數(shù)來(lái)完成。它們向任務(wù)隊(duì)列添加定時(shí)任務(wù)。 setTimeout()...
摘要:方法描述周期性地調(diào)用一個(gè)函數(shù)或者執(zhí)行一段代碼。方法可取消由方法設(shè)置的。語(yǔ)法詳解是該延時(shí)操作的數(shù)字此隨后可以用來(lái)作為方法的參數(shù)。需要注意的是,不支持第一種語(yǔ)法中向延遲函數(shù)傳遞額外參數(shù)的功能。該值標(biāo)識(shí)要取消的延遲執(zhí)行代碼塊。 方法 描述 setInterval 周期性地調(diào)用一個(gè)函數(shù)(function)或者執(zhí)行一段代碼。 clearInterval 取消掉用setI...
閱讀 3144·2021-11-11 16:54
閱讀 2323·2021-09-04 16:48
閱讀 3228·2019-08-29 16:08
閱讀 652·2019-08-29 15:13
閱讀 1355·2019-08-29 15:09
閱讀 2673·2019-08-29 12:45
閱讀 1937·2019-08-29 12:12
閱讀 461·2019-08-26 18:27