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