js 異步

瀏覽器(或者說JS引擎)執(zhí)行JS的機制是基于事件循環(huán)。

由于JS是單線程神得,所以同一時間能且只能執(zhí)行一個任務频轿,其他任務就得排隊,后續(xù)任務必須等到前一個任務結束才能開始執(zhí)行痊银。

為了避免因為某些長時間任務造成的無意義等待抵蚊,JS引入了異步的概念,用另一個線程來管理異步任務溯革。

同步任務直接在主線程隊列中順序執(zhí)行贞绳,而異步任務會進入另一個任務隊列,不會阻塞主線程致稀。等到主線程隊列空了(執(zhí)行完了)的時候冈闭,就會去異步隊列查詢是否有可執(zhí)行的異步任務了(異步任務通常進入異步隊列之后還要等一些條件才能執(zhí)行,如ajax請求抖单、文件讀寫)萎攒,如果某個異步任務可以執(zhí)行了便加入主線程隊列,以此循環(huán)臭猜。

先來看一下實現(xiàn)異步的一些方式躺酒,下面用分類的方式列舉一下
  1. 經(jīng)典的回調(diào)函數(shù)
  • callback
  1. 監(jiān)聽事件
  • onload
  • new Image
function asynByImg( callback ) {
  var img = new Image();
  img.onload = img.onerror = img.onreadystatechange = function() {
    img = img.onload = img.onerror = img.onreadystatechange = null;
    callback(); 
  }
  img.src = "data:image/png,";
}
asynByImg(function(){
  console.log(1);
});
console.log(2);
  • 事件綁定
  • 發(fā)布/訂閱模式
  • Message
  1. 延遲類
  • setTimeout
setTimeout(function() {
  console.log(1);
});
console.log(2);
  • setInterval
setInterval(function() {
  console.log(1);
});
console.log(2);
  • requestAnimationFrame
(function() {
  var requestAnimationFrame = 
    window.requestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.msRequestAnimationFrame;
  var startTime = window.mozAnimationStartTime || Date.now();
  requestAnimationFrame(draw);
  function draw (timestamp) {
    // 計算兩次重繪的時間間隔
    var drawStart = timestamp || Date.now();
    var diff = drawStart - startTime;
    startTime = drawStart;
    requestAnimationFrame(draw);
  }
})()
  • setImmediate
  • process.nextTick()
  1. 異步功能函數(shù)
  • promise
  • ajax
  • async / await
  • Worker
  • Co / Generator
這些方式的執(zhí)行順序是什么樣的?為什么蔑歌?

先看下面的示例:

console.log('start')

const interval = setInterval(() => {  
  console.log('setInterval')
}, 0)

setTimeout(() => {  
  console.log('setTimeout 1')
  Promise.resolve()
      .then(() => {
        console.log('promise 3')
      })
      .then(() => {
        console.log('promise 4')
      })
      .then(() => {
        setTimeout(() => {
          console.log('setTimeout 2')
          Promise.resolve()
              .then(() => {
                console.log('promise 5')
              })
              .then(() => {
                console.log('promise 6')
              })
              .then(() => {
                clearInterval(interval)
              })
        }, 0)
      })
}, 0)

Promise.resolve()
    .then(() => {  
        console.log('promise 1')
    })
    .then(() => {
        console.log('promise 2')
    })

上面的執(zhí)行結果依次打印下面的內(nèi)容

start
promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval
setTimeout 2
promise 5
promise 6
setInterval
羹应。。次屠。

想要知道上面的原理需要先來了解下面的幾個內(nèi)容

  1. 事件循環(huán)
    JavaScript在一個時間僅處理一個任務. 就是JavaScript在執(zhí)行時, 存在一個執(zhí)行隊列, 依次執(zhí)行隊列中的任務, 不能同時執(zhí)行多個任務园匹。主線程從"任務隊列"中讀取事件,這個過程是循環(huán)不斷的劫灶,所以整個的這種運行機制又稱為Event Loop(事件循環(huán))關于事件循環(huán)詳細說明可以查看這里裸违。
    這個過程用一個現(xiàn)實的例子就像是去銀行只有一個窗口,辦理業(yè)務就要排隊本昏,好痛苦供汛。但是如果一個任務需要執(zhí)行很久很久,腫么辦涌穆?等著唄怔昨。但是聰明的程序員怎么會忍受一個任務執(zhí)行那么長時間,于是把任務分成了macrotasks和microtasks兩類宿稀。
  2. macrotasks和microtasks
    macrotasks【同步任務】: script(整體代碼),setTimeout, setInterval, setImmediate, I/O, UI rendering
    microtasks【異步任務】: process.nextTick, Promises, Object.observe, MutationObserver
    同步任務以及異步任務會進入不同的“場所”趁舀,同步的進入主線程,異步的進入Event Table并注冊函數(shù)祝沸。當指定的事情完成時矮烹,Event Table會將這個函數(shù)移入Event Queue越庇。主線程執(zhí)行完畢為空,就會去Event Queue讀取對應的函數(shù)奉狈,進入主線程執(zhí)行卤唉。

然后解析上面代碼的執(zhí)行:
1. 同步任務直接放入到主模塊(主線程)任務隊列執(zhí)行. 異步任務掛起后臺執(zhí)行, 等待IO事件完成或行為事件被觸發(fā).
2. 系統(tǒng)后臺執(zhí)行異步任務, 如果某個異步任務事件發(fā)生(或者是行為事件被觸發(fā)), 則將該任務push到任務隊列中, 每個任務會對應一個回調(diào)函數(shù)進行處理. 這個步驟在后臺一直執(zhí)行, 因為就不斷有事件被觸發(fā), IO不斷完成, 任務被不斷的加入到任務隊列中.
3. 執(zhí)行任務隊列中的任務. 任務的具體執(zhí)行是在執(zhí)行棧中完成的. 當運行棧中一個任務的基本運行單元(稱之為Frame, 楨)全部執(zhí)行完畢后, 去讀取任務隊列中的下一個任務, 繼續(xù)執(zhí)行. 是一個循環(huán)的過程. 處理一個任務隊列中的任務, 稱之為一個tick.

即 macrotasks執(zhí)行console.log('start') 執(zhí)行后打印了‘start’; 然后macrotasks完成進入microtasks,執(zhí)行了console.log('promise 1')console.log('promise 2');microtasks執(zhí)行完成返回查看macrotasks執(zhí)行了console.log('setInterval')console.log('setTimeout 1'); microtasks執(zhí)行console.log('promise 3')console.log('promise 4'); 再次進入macrotasksconsole.log('setInterval')console.log('setTimeout 2');最后執(zhí)行microtasks隊列console.log('promise 5')console.log('promise 6')嘹吨,到這里microtasks全部完成搬味,后面執(zhí)行macrotasks

備注:事件隊列是遵循先進先出, 需要依次處理. 所以, 定時器函數(shù)運行時,如果遇到了定時的事件, 事件發(fā)生, 也僅僅是將該任務push入任務隊列而已(并沒有立即執(zhí)行回調(diào)函數(shù))蟀拷。同時這也是為什么setTimeout預定的時間不一定會執(zhí)行碰纬,例如你預定300ms后執(zhí)行,它最早是300ms的原因

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末问芬,一起剝皮案震驚了整個濱河市悦析,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌此衅,老刑警劉巖强戴,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異挡鞍,居然都是意外死亡骑歹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門墨微,熙熙樓的掌柜王于貴愁眉苦臉地迎上來道媚,“玉大人,你說我怎么就攤上這事翘县∽钣颍” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵锈麸,是天一觀的道長镀脂。 經(jīng)常有香客問我,道長忘伞,這世上最難降的妖魔是什么薄翅? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮氓奈,結果婚禮上匿刮,老公的妹妹穿的比我還像新娘。我一直安慰自己探颈,他們只是感情好,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布训措。 她就那樣靜靜地躺著伪节,像睡著了一般光羞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上怀大,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天纱兑,我揣著相機與錄音,去河邊找鬼化借。 笑死潜慎,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的蓖康。 我是一名探鬼主播铐炫,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蒜焊!你這毒婦竟也來了倒信?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤北专,失蹤者是張志新(化名)和其女友劉穎啦粹,沒想到半個月后官份,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡乘综,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了套硼。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卡辰。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖熟菲,靈堂內(nèi)的尸體忽然破棺而出看政,到底是詐尸還是另有隱情,我是刑警寧澤抄罕,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布允蚣,位于F島的核電站,受9級特大地震影響呆贿,放射性物質(zhì)發(fā)生泄漏嚷兔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一做入、第九天 我趴在偏房一處隱蔽的房頂上張望冒晰。 院中可真熱鬧,春花似錦竟块、人聲如沸壶运。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蒋情。三九已至埠况,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間棵癣,已是汗流浹背辕翰。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留狈谊,地道東北人喜命。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像河劝,于是被迫代替她去往敵國和親壁榕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348