前言:對定時(shí)器的小整理。
JS單線程
Javascript語言的執(zhí)行環(huán)境是"單線程"(single thread)。也就是說,瀏覽器只分配給js一個(gè)主線程用來執(zhí)行任務(wù)即函數(shù),但是每次只能執(zhí)行一個(gè)任務(wù),只有等到當(dāng)前任務(wù)執(zhí)行完成后,才執(zhí)行后面的任務(wù),這些任務(wù)形成一個(gè)任務(wù)隊(duì)列排隊(duì)等候執(zhí)行
為了避免因?yàn)槟承╅L時(shí)間任務(wù)造成的無意義等待,JS引入了異步的概念,用另一個(gè)線程來管理異步任務(wù)。
同步任務(wù)直接在主線程隊(duì)列中順序執(zhí)行,而異步任務(wù)會(huì)進(jìn)入另一個(gè)任務(wù)隊(duì)列,不會(huì)阻塞主線程。等到主線程隊(duì)列空了(執(zhí)行完了)的時(shí)候,就會(huì)去異步隊(duì)列查詢是否有可執(zhí)行的異步任務(wù)了(異步任務(wù)通常進(jìn)入異步隊(duì)列之后還要等一些條件才能執(zhí)行,如ajax請求、文件讀寫),如果某個(gè)異步任務(wù)可以執(zhí)行了便加入主線程隊(duì)列,以此循環(huán)。
JS定時(shí)器
定時(shí)器也是一種異步任務(wù),通常瀏覽器都有一個(gè)獨(dú)立的定時(shí)器模塊,定時(shí)器的延遲時(shí)間就由定時(shí)器模塊來管理,當(dāng)某個(gè)定時(shí)器到了可執(zhí)行狀態(tài),就會(huì)被加入主線程隊(duì)列。
基本計(jì)時(shí)器
- setInterval() :以指定時(shí)間為周期循環(huán)執(zhí)行
- setTimeout(): 只在指定時(shí)間后執(zhí)行一次
寫法
- setInterval(expression,milliseconds)
- setTimeout(expression,milliseconds)
- 其中expression既可以是字符串,也可以是匿名函數(shù),或者是一個(gè)函數(shù)名,milliseconds是延時(shí)或者重復(fù)執(zhí)行的毫秒數(shù)
例子
setTimeout(function () {
console.log('timeout');
}, 1000);
setInterval(function () {
console.log('interval')
}, 1000);
// 輸出一次 timeout,每隔1S輸出一次 interval
清除定時(shí)器
- 定時(shí)器清除的方法:clearInterval(str)和clearTimeout(str)
- 定時(shí)器有個(gè)返回值,該數(shù)字代表定時(shí)器的序號,即第多少個(gè)定時(shí)器,我們獲取其返回值后,調(diào)用clearInterval(返回值)或clearTimeout(返回值), 即可停止計(jì)時(shí)器
- 要清除定時(shí)器,就必須在用定時(shí)器的時(shí)候,定義一個(gè)變量來記錄定時(shí)器的返回值
定時(shí)器的返回值:
var timer1=window.setTimeout(function(){
console.log("我是第一個(gè)定時(shí)器")
},1000);
console.log(timer1);//1
var timer2=window.setTimeout(function(){
console.log("我是第二個(gè)定時(shí)器")
},1000);
console.log(timer2);//2
清除定時(shí)器:
var id=window.setInterval("somefunction",10000);
//取消定時(shí)執(zhí)行
window.clearInterval(id);
id=null;
注意:
- setTimeout雖然只執(zhí)行一次,但執(zhí)行后,定時(shí)器還在,只是沒用了而已
- 定時(shí)器即使清除了,其返回值也不會(huì)清除,之后設(shè)置的定時(shí)器的返回值也會(huì)在其返回值的基礎(chǔ)上繼續(xù)向后排
- 一般清空定時(shí)器后會(huì)將清空的定時(shí)器的變量置空,這樣寫既可以釋放內(nèi)存,也可以便于后邊代碼的判斷。
定時(shí)器傳遞參數(shù)
先看一下定時(shí)器的常用寫法:
- 函數(shù)名,不帶參數(shù)
setInterval(test,1000); //1秒后執(zhí)行- 字符串,可以執(zhí)行的代碼
setInterval('test()',1000); //1秒后執(zhí)行- 匿名函數(shù)
setInterval(function(){},1000); //1秒后執(zhí)行- 調(diào)用函數(shù)
setInterval(test(),1000); //立即執(zhí)行
在使用函數(shù)名作為調(diào)用句柄時(shí)都不能帶參數(shù),而在許多場合必須要帶參數(shù)
函數(shù)hello
var userName="jack";
//根據(jù)用戶名顯示歡迎信息
function hello(_name){
alert("hello,"+_name);
}
window.setTimeout(hello(userName),3000);//會(huì)立即執(zhí)行,不是等3秒后執(zhí)行
修改:
window.setTimeout("hello(userName)",3000);
注:其他方法見
異步之定時(shí)器
setTimeout為例:
setTimeout(function(){
console.log(0);
},0)
console.log(1);
// 1
// 0
- 大家可能疑問了,setTimeout中設(shè)置的推遲執(zhí)行的毫秒數(shù)是0呀,不就是立即執(zhí)行的意思嗎。實(shí)際在執(zhí)行程序的時(shí)候,瀏覽器會(huì)默認(rèn)setTimeout以及ajax請求這一類的方法都是耗時(shí)程序(盡管可能不耗時(shí)),所以此時(shí)的setTimeout盡管它推遲時(shí)間為0,但是js不會(huì)立即執(zhí)行,而是把它加入任務(wù)隊(duì)列,當(dāng)執(zhí)行完執(zhí)行棧的同步任務(wù)也就是打印1后,再執(zhí)行setTimeout的回調(diào)函數(shù),打印0。
- 總之,setTimeout(fn,0)的含義是,指定某個(gè)任務(wù)在主線程最早可得的空閑時(shí)間執(zhí)行,也就是說,盡可能早得執(zhí)行。它在"任務(wù)隊(duì)列"的尾部添加一個(gè)事件,因此要等到同步任務(wù)和"任務(wù)隊(duì)列"現(xiàn)有的事件都處理完,才會(huì)得到執(zhí)行。
- 所以注意的是,setTimeout()只是將事件插入了任務(wù)隊(duì)列,必須等到當(dāng)前代碼(執(zhí)行棧)執(zhí)行完,主線程才會(huì)去執(zhí)行它指定的回調(diào)函數(shù)。但如果當(dāng)前任務(wù)十分耗時(shí),需要等很久,所以并沒有辦法保證,回調(diào)函數(shù)一定會(huì)在setTimeout()指定的時(shí)間執(zhí)行,比如說你指定10ms后執(zhí)行,但是當(dāng)前的任務(wù)執(zhí)行了20ms,所以setTimeout的回調(diào)函數(shù)并不能在10ms后立即執(zhí)行,可能要20ms后,如果setTimeout在任務(wù)隊(duì)列中不是排第一位,可能還不止20ms。