Javascript 異步編程(三)定時(shí)器

Javascript 異步編程(三)

并行阱驾?并發(fā)?異步昔榴?

同步:synchronous: 指所有任務(wù)按出現(xiàn)的先后順序依次執(zhí)行 如果出現(xiàn)阻塞的任務(wù)逞怨,那么線程就會(huì)等待這個(gè)任務(wù)完成,接著執(zhí)行下一個(gè)任務(wù)槽惫。

異步:asynchronous:不保證所有任務(wù)按出現(xiàn)的順序執(zhí)行

并發(fā):concurrent:從宏觀上周叮,某個(gè)時(shí)間段里面多個(gè)程序都得到了運(yùn)行,但不是說(shuō)“同時(shí)運(yùn)行”

并行:parallel:在多核心下界斜,因進(jìn)程和線程獨(dú)立運(yùn)行仿耽,且多個(gè)線程之間共享數(shù)據(jù),程序可以同時(shí)運(yùn)行各薇。

定時(shí)器

常用的回調(diào)函數(shù)有:

  • setTimeout
  • setInterval
  • setImmediate(Node.js)
  • requestAnimationFrame

https://zhuanlan.zhihu.com/p/55129100

setTimeout

作用:延遲指定的時(shí)間來(lái)調(diào)用函數(shù)或計(jì)算表達(dá)式项贺。

語(yǔ)法:setTimeout(func /**函數(shù),必選*/,code /**表達(dá)式君躺,可選*/ ,milliseconds /**執(zhí)行需等待的毫秒數(shù),必選*/param1, param2, .../**傳遞給函數(shù)的參數(shù)开缎,可選*/)

具體用法參加《Javascript異步編程(一)》

setInterval

作用:按照指定的周期(以毫秒計(jì))來(lái)調(diào)用函數(shù)或計(jì)算表達(dá)式棕叫。

語(yǔ)法:setInterVal(func /**函數(shù),必選*/,code /**表達(dá)式,可選*/ ,milliseconds /**每次執(zhí)行將延遲的毫秒數(shù)奕删,必選*/param1, param2, .../**傳遞給函數(shù)的參數(shù)俺泣,可選*/)

原理

 console.log('sync...',1);
  setInterval(()=>{
    console.log('sync...',2)
  },2000);
  console.log('sync...',3)

流程分析:

  1. 主程序調(diào)用棧
  2. 調(diào)用webAPI-setInterVal
  3. 定時(shí)器線程計(jì)數(shù)2s
  4. 每隔2s事件觸發(fā)線程將回調(diào)放入任務(wù)隊(duì)列
  5. 主線程通過(guò)Event Loop遍歷任務(wù)隊(duì)列執(zhí)行回調(diào) console.log('sync...',2)

注意

  • IE9及以下版本不兼容(setTimeout&setInterval)傳遞額外參數(shù),可使用polyfill
  • setInterval完残,delay最小為10ms伏钠。意味著無(wú)法設(shè)置為0秒間隔,但可以嘗試使用postMessage實(shí)現(xiàn)
  • 使用clearInterval(timerId)關(guān)閉指定定時(shí)器
  • 確苯魃瑁回調(diào)函數(shù)執(zhí)行時(shí)長(zhǎng)小于delay時(shí)間
  • 同setTimeout一樣熟掂,setInterval回調(diào)中的this永遠(yuǎn)指向global
  • 不推薦使用setInterval(code,delay),有安全風(fēng)險(xiǎn)扎拣。

為什么不建議使用setInterval

如果任務(wù)實(shí)際耗時(shí)超過(guò)delay,會(huì)出現(xiàn)同一時(shí)間觸發(fā)多個(gè)回調(diào)赴肚。

有以下場(chǎng)景,每隔1s調(diào)用服務(wù)

// 時(shí)間間隔大于delay
let count = 5
let intervalTimer = setInterval(function () {
  if (count <= 0) {
    clearInterval(intervalTimer)
    return
  }
  /*模擬延時(shí)任務(wù)*/
  let timeoutTimer = setTimeout(function (count) {
    console.log(`the ${count} is running`)
    clearTimeout(timeoutTimer)
  }, Math.floor(Math.random() * (10000) + 1000),count)
  count--
}, 1000)
image_9.png

從上圖可知二蓝,xhr響應(yīng)無(wú)法按照順序返回誉券,這樣就會(huì)導(dǎo)致無(wú)法正常處理結(jié)果

折中方案

function moreBetterInterval (count) {
  // 1s后調(diào)用
  setTimeout(function (countDown) {
    console.log(count +' is begin')
    let timeoutTimer = setTimeout(function (times) {
      console.log(`the ${times} is running`)
      clearTimeout(timeoutTimer)
      times--;
      if(times>0){
        moreBetterInterval(times)
      }
    }, Math.floor(Math.random() * (10000) + 1000),countDown)
  }, 1000,count)
}
moreBetterInterval(5)
image_10.png

可以保證遞歸之前已執(zhí)行完回調(diào),但無(wú)法保證按照一定的時(shí)間間隔侣夷。

實(shí)際應(yīng)用場(chǎng)景

  • 短信倒計(jì)時(shí)
let countDown=60;
let timer=setInterval(function () {
  countDown--;
  if(countDown<0){
    clearInterval(timer);
    countDown=60
  }
},1000)
  • 顯示當(dāng)前時(shí)間
setInterval(function () {
    let d = new Date()
    $('#clock')[0].innerHTML = d.toLocaleTimeString()
}, 1000)

setImmediate

僅在Internet Explorer和Node.js下可用

作用:在循環(huán)事件任務(wù)完成后馬上運(yùn)行指定代碼

語(yǔ)法:setImmediate(func /**函數(shù),必選*/,code /**表達(dá)式横朋,可選*/,[ param1,param2百拓,...]/**傳遞給函數(shù)的參數(shù)琴锭,可選*/);

setImmediate與setTimeout(func,0)?

setTimeout(func,0)

需要考慮

  • 是在哪個(gè)階段注冊(cè)的,如果在定時(shí)器回調(diào)或者I/O回調(diào)里面俊鱼,setImmediate肯定先執(zhí)行刻像。如果在最外層或者setImmediate回調(diào)里面,哪個(gè)先執(zhí)行取決于當(dāng)時(shí)機(jī)器狀況并闲。
  • 當(dāng)前的Event Loop的任務(wù)隊(duì)列的情況细睡,如果在隊(duì)尾,那也要先執(zhí)行前面的事件帝火,這樣也無(wú)法保證立即執(zhí)行

requestAnimationFrame

作用:接受一個(gè)動(dòng)畫(huà)執(zhí)行函數(shù)作為參數(shù)溜徙,這個(gè)函數(shù)的作用是僅執(zhí)行一幀動(dòng)畫(huà)的渲染湃缎,并根據(jù)條件判斷是否結(jié)束,如果動(dòng)畫(huà)沒(méi)有結(jié)束蠢壹,則繼續(xù)調(diào)用requestAnimationFrame并將自身作為參數(shù)傳入

語(yǔ)法:requestAnimationFrame(func /**函數(shù)嗓违,必選*/)

細(xì)節(jié):以60FPS(每幀16.7ms)為目標(biāo),瀏覽器內(nèi)部會(huì)選擇渲染的最佳時(shí)機(jī)

與setTimeout動(dòng)畫(huà)區(qū)別:

  • setTimeout(func,16.7):容易卡頓

原因有兩個(gè):

  • 實(shí)際執(zhí)行時(shí)間晚于設(shè)定的延遲時(shí)間,出現(xiàn)卡頓
  • 與瀏覽器刷新率有關(guān)图贸,不同設(shè)備的屏幕刷新頻率可能會(huì)不同蹂季,而setTimeout只能設(shè)定固定的時(shí)間間隔,無(wú)法保證與刷新率同步疏日,容易丟幀
  • requestAnimationFrame能節(jié)省CPU開(kāi)銷(xiāo)乏盐,當(dāng)元素隱藏或不可見(jiàn)時(shí),會(huì)停止渲染制恍。而setTimeout仍在后臺(tái)執(zhí)行。

用途

  • 實(shí)現(xiàn)一幀的函數(shù)節(jié)流
  • 動(dòng)畫(huà)

注意

  • 使用cancelAnimationFrame(id)關(guān)閉渲染動(dòng)畫(huà)
  • IE10以下不兼容神凑,可使用setTimeout進(jìn)行polyfill 從而模擬幀率盡量適配刷新率

結(jié)尾

通過(guò)以上定時(shí)器净神,最顯著的共同點(diǎn)是:回調(diào)。

初一看溉委,回調(diào)沒(méi)有問(wèn)題呀鹃唯,可以延遲計(jì)算。請(qǐng)想下以下情景:

  1. 嵌套回調(diào)
let msg = document.getElementById('msg')
$('#btn').click(function (evt) {
  msg.innerHTML += `${new Date()} processing btn click callback... <br>`
  setTimeout(function request () {
    msg.innerHTML += `${new Date()} processing setTimeout callback...<br>`
    $.get('https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js', function (data, status) {
      msg.innerHTML += `${new Date()} processing ajax callback...<br>`
    })
  }, 500)
}) 

這里我們用了三個(gè)函數(shù)嵌套瓣喊,這種代碼就被稱(chēng)為“回調(diào)地獄(callback hell)”坡慌,這樣的代碼難以編寫(xiě),難以理解而且難以維護(hù)

  1. 控制反轉(zhuǎn)
$.get('https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js', function (data, status) {
  msg.innerHTML += `${new Date()} processing ajax callback...<br>`
})

即自己程序的一部分的執(zhí)行控制權(quán)交由某個(gè)第三方藻三。在你不確定這個(gè)回調(diào)能否按照預(yù)期執(zhí)行時(shí)洪橘,發(fā)生意外時(shí)很難定位問(wèn)題。

為了優(yōu)雅的的處理回調(diào)最大的問(wèn)題:控制反轉(zhuǎn)棵帽,有以下方式:

  • 回調(diào)分離 -->ES6 Promise
  • error-first風(fēng)格-->Node.js中會(huì)將回調(diào)的第一個(gè)參數(shù)保留用作error
const fs=require('fs')
fs.readFile(__dirname,function(err,data) {
  if (err)  console.log(err)
  //...
})

但還是不優(yōu)雅熄求,并沒(méi)有真正解決我們的控制反轉(zhuǎn)問(wèn)題,只是將我們之前擔(dān)心的程序異常暴露了出來(lái)逗概。

可能現(xiàn)在你希望有API或其他語(yǔ)言機(jī)制來(lái)解決這些問(wèn)題弟晚。所幸,ES6會(huì)給你帶來(lái)些干貨~

Reference

HTML Timers
Timer resolution in browsers

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末逾苫,一起剝皮案震驚了整個(gè)濱河市卿城,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌铅搓,老刑警劉巖瑟押,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異狸吞,居然都是意外死亡勉耀,警方通過(guò)查閱死者的電腦和手機(jī)指煎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)便斥,“玉大人至壤,你說(shuō)我怎么就攤上這事∈嗑溃” “怎么了像街?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)晋渺。 經(jīng)常有香客問(wèn)我镰绎,道長(zhǎng),這世上最難降的妖魔是什么木西? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任畴栖,我火速辦了婚禮,結(jié)果婚禮上八千,老公的妹妹穿的比我還像新娘吗讶。我一直安慰自己,他們只是感情好恋捆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布照皆。 她就那樣靜靜地躺著,像睡著了一般沸停。 火紅的嫁衣襯著肌膚如雪膜毁。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天愤钾,我揣著相機(jī)與錄音瘟滨,去河邊找鬼。 笑死能颁,一個(gè)胖子當(dāng)著我的面吹牛室奏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播劲装,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼胧沫,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了占业?” 一聲冷哼從身側(cè)響起绒怨,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谦疾,沒(méi)想到半個(gè)月后南蹂,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡念恍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年六剥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了晚顷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡疗疟,死狀恐怖该默,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情策彤,我是刑警寧澤栓袖,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站店诗,受9級(jí)特大地震影響裹刮,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜庞瘸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一捧弃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧擦囊,春花似錦塔橡、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)户辞。三九已至泌类,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間底燎,已是汗流浹背刃榨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留双仍,地道東北人枢希。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像朱沃,于是被迫代替她去往敵國(guó)和親苞轿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

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