setTimeout異步

簡單解釋單線程至朗、任務(wù)隊列的概念

單線程:JavaScript是一個單線程語言,瀏覽器只會分配一個js引擎線程來解析和執(zhí)行js同步代碼屉符。即任務(wù)是串行的,后一個任務(wù)需要等待前一個任務(wù)的執(zhí)行锹引。

任務(wù)隊列:所有同步任務(wù)都在主線程上執(zhí)行矗钟,形成一個執(zhí)行棧。主線程之外嫌变,還存在一個“任務(wù)隊列”真仲,指定過回調(diào)函數(shù)的事件發(fā)生時就會進(jìn)入“任務(wù)隊列”,等待主線程讀取初澎。線程把棧中任務(wù)做完之后秸应,就會來看“任務(wù)隊列”中的事件,“任務(wù)隊列”是一個先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)碑宴,排在前面的事件優(yōu)先被主線程讀取软啼。讀取過程基本上是自動的,只要執(zhí)行棧一清空延柠,“任務(wù)隊列”上第一位的事件就自動進(jìn)入主線程祸挪。但是,如果包含“定時器”贞间,主線程首先要檢查一下執(zhí)行時間贿条,某些事件只有到了規(guī)定的時間雹仿,才能返回主線程。主線程從“任務(wù)隊列”中讀取事件整以,這個過程是循環(huán)不斷的胧辽,所以整個的運行機(jī)制又稱為“Event Loop”。

setTimeout:N毫秒之后執(zhí)行某個函數(shù)公黑,一次一個ID邑商,實際延遲時間比N毫秒久,會在其他的運行完了以后最后來看setTimeout的內(nèi)容凡蚜,可想象為一個鬧鐘人断,設(shè)定了30秒后吃飯,30秒之后鬧鐘響了朝蜘,但當(dāng)時還在寫作業(yè)恶迈,那么會等作業(yè)寫完之后再來看鬧鐘的內(nèi)容并去執(zhí)行它。
setInterval:每隔N毫秒執(zhí)行某個函數(shù)谱醇,只有一個ID

瀏覽器對定時器setTimeout很懈怠蝉绷,當(dāng)用戶焦點離開該界面時,瀏覽器久變懶了枣抱,原本設(shè)置500ms做一件事情熔吗,但你沒有看它,它可能就1000ms才去做一件事情佳晶。

異步:異步就是一個猴急的人不愿意等桅狠,于是叫了一個黃牛(回調(diào)函數(shù))幫他等結(jié)果。
異步和回調(diào)一般同時出現(xiàn)轿秧。

比如排隊取號中跌,一個猴急的人不愿意等,但排隊取號是不可能馬上拿到號碼的菇篡,因為他不可能拿到未來的東西漩符。即用同步的方式無法拿到結(jié)果。
所以他派了一個黃牛(函數(shù))幫他排隊驱还,他自己去干別的事情嗜暴,等黃牛拿到了再把結(jié)果告訴他。

function 排隊取號(黃牛) {
    setTimeout(function f2() {
        黃牛('你的號碼是:233')
    }, 3000)
}
function 黃牛(result) {
    console.log(result)
}
排隊取號(黃牛)

黃牛是我的议蟆,所以黃牛拿到的就是我拿到的闷沥,如果你不清楚為什么,那就改寫一下代碼:

function 排隊取號(黃牛) {
    setTimeout(function f2() {
        黃牛('你的號碼是:233')
    }, 3000)
}

//上面和下面分開看
var 我的號 = undefined
function 黃牛(result) {
    我的號 = result
    console.log(我的號)
}
排隊取號(黃牛)

面試題:

for(var i=0;i<5;i++){
    console.log(i)//0,1,2,3,4
}
for (var i = 0; i < 5; i++) {
    (function (i) {//這一行的i是一個新的變量i咐容,也可以叫j
        setTimeout(function () {
            console.log(i);//打印的i是新的變量i舆逃,這個i是全局中傳遞進(jìn)來的,沒有進(jìn)行自增操作
        }, i * 1000);
    })(i);//把全局的i的值傳遞給函數(shù)中的i或者j
}//大約0s后打出0,大約1s后打出1,大約2s后打出2路狮,大約3s后打出3虫啥,大約4s后打出4
for (var i = 0; i < 5; i++) {
    setTimeout((function (i) {
        console.log(i);
    })(i), i * 1000);
}

//首先改寫代碼
for (var i = 0; i < 5; i++) {
    var t1 = function (i) {
        console.log(i);//t1的返回值是undefined
    }
    var t2 = t1(i)//t2是調(diào)用t1的結(jié)果
    var t3 = i * 1000//0,1000,2000,3000,4000
    setTimeout(t2, t3);
}
//五次循環(huán)分別執(zhí)行了五次setTimeout。分別是
//setTimeout(undefined,0)
//setTimeout(undefined,1000)
//setTimeout(undefined,2000)
//setTimeout(undefined,3000)
//setTimeout(undefined,4000)
//產(chǎn)生了undefined但并沒有打印undefined奄妨,運行的5次打印了5次t1(i)涂籽,這個i是新建的局部變量i,不是全局中的展蒂,全局中的i值會賦值給局部變量的i
//最終結(jié)果是0,1,2,3,4,沒有延時苔咪,因為setTimeout第一個參數(shù)為undefined锰悼,所以定時器什么也沒做。
for(var i=0;i<5;i++){
    setTimeout(function(){
        console.log(i)
    },1000*i);
}//setTimeout是異步团赏,js引擎在for循環(huán)結(jié)束后才會開始執(zhí)行setTimeout的內(nèi)容箕般,也就是說console.log(i)之前for循環(huán)已經(jīng)結(jié)束了,i變?yōu)?舔清,所以結(jié)果是:大約0s后打出5,大約1s后打出5丝里,大約2s后打出5,大約3s后打出5体谒,大約4s后打出5
for (var i = 0; i < 5; i++) {
    (function () {
        setTimeout(function () {
            console.log(i);
        }, i * 1000);
    })(i);
}//結(jié)果是:大約0s后打出5,大約1s后打出5杯聚,大約2s后打出5,大約3s后打出5抒痒,大約4s后打出5

//如果覺得不好理解可以將一行拆分為多行幌绍,聲明一個函數(shù)t,調(diào)用這個函數(shù)t
for (var i = 0; i < 5; i++) {
    function t() {
        setTimeout(function () {
            console.log(i);
        }, i * 1000);
    }
    t(i);
}

做一個倒計時器

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  <select name="" id="mySelect" placeholder="選擇一個時間">
    <option value="1" selected>1分鐘</option>
    <option value="5">5分鐘</option>
    <option value="10">10分鐘</option>
    <option value="20">20分鐘</option>
  </select>
  <button id="startButton">start</button>
  <button id="pauseButton" disabled>pause</button>
  <button id="resumeButton" disabled>resume</button>
  <div id="outputDiv">

  </div>
  <script>
    var timeLeft = 10
    let lastTimerID

    function showTime() {
      //通過id名可以直接獲取到頁面上的元素
      outputDiv.textContent = timeLeft + '秒';
      if (timeLeft === 0) return
      timeLeft -= 1
      lastTimerID = setTimeout(showTime, 1000)
    }

    startButton.onclick = function () {
      var valueNumber = parseInt(mySelect.value, 10)
      var seconds = valueNumber * 60
      timeLeft = seconds
      if (lastTimerID) {
        window.clearTimeout(lastTimerID)
      }
      showTime()
      pauseButton.disabled = false
    }

    pauseButton.onclick = function () {
      if (lastTimerID) {
        window.clearTimeout(lastTimerID)
        resumeButton.disabled = false
        pauseButton.disabled = true
      }
    }

    resumeButton.onclick = function () {
      showTime()
      pauseButton.disabled = false
      resumeButton.disabled = true
    }
  </script>
</body>

</html>

面試題

var startTime = +(new Date()),endTime;
setTimeout(function(){
    endTime = +(new Date());
    console.log(1);
    console.log(endTime - startTime);
    },3000);
setTimeout(function(){
    endTime = +(new Date());
    console.log(2);
    console.log(endTime - startTime);
    },2000);
while(+(new Date()) - startTime < 5000){}
console.log(+(new Date()) - startTime);

請問alert(1)和alert(2)的先后順序和時間間隔故响。

5s鐘的同步執(zhí)行完之后傀广,開始執(zhí)行setTimeout異步代碼,因為第二個插入的時間間隔早彩届,先執(zhí)行伪冰。

因為5s時間超過了3s,所以兩個函數(shù)幾乎同時執(zhí)行樟蠕。如果同步時間是1s贮聂,那就是第2s之后執(zhí)行第二個函數(shù),第3s后執(zhí)行第一個函數(shù)寨辩,代碼運行總時長3s多一點點

setTimeout(..) 并沒有把你的回調(diào)函數(shù)掛在事件循環(huán)隊列中寂汇。它所做的是設(shè) 定一個定時器。
當(dāng)定時器到時后,環(huán)境會把你的回調(diào)函數(shù)放在事件隊列中,如果這時候事件循環(huán)中已經(jīng)有 20 個項目了會怎樣呢?你的回調(diào)就會等待,定時器只能確保你的回調(diào)函數(shù)不會在指定的 時間間隔之前運行,但可能會在那個時刻運行,也可能在那之后運行,要根據(jù)事件隊列的 狀態(tài)而定(PS: 這就是造成定時器不準(zhǔn)確的緣由)捣染。

setTimeout(..0)(hack)進(jìn)行異步調(diào)度,基本上它的意思就是把這個函數(shù)插入到當(dāng)前事件循環(huán)隊列的結(jié)尾處骄瓣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子榕栏,更是在濱河造成了極大的恐慌畔勤,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扒磁,死亡現(xiàn)場離奇詭異庆揪,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)妨托,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進(jìn)店門缸榛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人兰伤,你說我怎么就攤上這事内颗。” “怎么了敦腔?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵均澳,是天一觀的道長。 經(jīng)常有香客問我符衔,道長找前,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任判族,我火速辦了婚禮躺盛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘形帮。我一直安慰自己颗品,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布沃缘。 她就那樣靜靜地躺著躯枢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪槐臀。 梳的紋絲不亂的頭發(fā)上锄蹂,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天,我揣著相機(jī)與錄音水慨,去河邊找鬼得糜。 笑死,一個胖子當(dāng)著我的面吹牛晰洒,可吹牛的內(nèi)容都是我干的朝抖。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼谍珊,長吁一口氣:“原來是場噩夢啊……” “哼治宣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤侮邀,失蹤者是張志新(化名)和其女友劉穎坏怪,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绊茧,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡铝宵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了华畏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹏秋。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖亡笑,靈堂內(nèi)的尸體忽然破棺而出侣夷,到底是詐尸還是另有隱情,我是刑警寧澤况芒,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布惜纸,位于F島的核電站叶撒,受9級特大地震影響绝骚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜祠够,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一压汪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧古瓤,春花似錦止剖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至绎速,卻和暖如春皮获,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纹冤。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工洒宝, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人萌京。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓雁歌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親知残。 傳聞我的和親對象是個殘疾皇子靠瞎,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,554評論 2 349

推薦閱讀更多精彩內(nèi)容