前端深入瀏覽器架構(2)

消息隊列和事件循環(huán)

每一個渲染進程都有一個主線程,既要處理DOM擒滑,又要計算樣式鸠补,還要處理布局,同時還需要處理js任務以及各種輸入事件藐翎。要讓這么多不同類型的任務在主線程中有條不紊執(zhí)行材蹬,需要一個系統(tǒng)來統(tǒng)籌調度,這個系統(tǒng)就是消息隊列和事件循環(huán)系統(tǒng)

隊列:先進先出

62.png
  • 一個消息隊列用來調度任務吝镣,接收其他線程的任務
  • IO線程用來接收其他進程產生的任務
  • 渲染主線程會循環(huán)的從消息隊列頭部中讀取任務堤器,執(zhí)行任務

消息隊列中的任務類型

  • 輸入事件(鼠標滾動、點擊末贾、移動)
  • 微任務
  • 文件讀寫
  • websocket
  • js定時器
  • js執(zhí)行闸溃,解析DOM,樣式計算拱撵,布局計算辉川,css動畫

宏任務微任務

消息隊列中的任務執(zhí)行過程中,會產生新的任務拴测,如果將這些新產生的任務添加到消息隊列尾部乓旗,那么這些任務會喪失實時性,如果任務直接出發(fā)集索,阻斷當前任務的執(zhí)行屿愚,又會影響當前任務的執(zhí)行效率

所以最好的時機是:當前任務執(zhí)行完,下個任務開始前务荆;

eg: dom節(jié)點的監(jiān)控妆距;

消息隊列中的任務稱為宏任務,每個宏任務都包含了一個微任務隊列函匕,在執(zhí)行宏任務的過程中娱据,產生的微任務會添加到列表中,當宏任務主要功能執(zhí)行完后盅惜,會執(zhí)行當前宏任務的微任務列表

宏任務

  • 渲染事件(如解析DOM忌穿、計算布局、繪制)
  • 用戶交互事件(如鼠標點擊伴网、滾動頁面、放大縮小等)
  • JavaScript腳本執(zhí)行事件
  • 網絡請求完成澡腾、文件讀寫完成事件

宏任務可以滿足我們大部分的日常需求,不過如果有對時間精度要求較高的要求糕珊,宏任務就難以勝任了。
頁面的渲染事件红选、各種IO的完成事件、執(zhí)行js腳本的事件喇肋、用戶交互的事件等都隨時有可能被添加到消息隊列中,而且添加事件是由系統(tǒng)操作的蝶防,js代碼不能準確掌控任務要添加到隊列中的位置甚侣,因此很難控制開始執(zhí)行任務的時間间学。

微任務

微任務就是一個需要異步執(zhí)行的函數,執(zhí)行時機是在主函數執(zhí)行結束之后低葫、當前宏任務結束之前

  1. 微任務的產生

    • 使用MutationObserver監(jiān)控某個DOM節(jié)點,通過JS修改這個節(jié)點嘿悬,當DOM發(fā)生變化時,就會產生DOM變化記錄的微任務

    • Promise善涨,當調用Promise.resolve()或者reject時

  2. 微任務的執(zhí)行

    • 宏任務快執(zhí)行完成時主到,也就在js引擎準備退出全局執(zhí)行上下文并清空調用棧的時候,js會檢查全局執(zhí)行上下文的微任務隊列

    • 在執(zhí)行微任務的過程中躯概,產生了新的微任務,同樣會將該微任務添加到微任務隊列中畔师,v8引擎一直循環(huán)執(zhí)行微任務隊列中的任務娶靡,直到隊列為空才算執(zhí)行結束

63.png
  • 微任務和宏任務是綁定的,每個宏任務執(zhí)行時看锉,會創(chuàng)建自己的微任務隊列
  • 微任務的執(zhí)行時長會影響到當前宏任務的時長姿锭。
  • 在一個宏任務中塔鳍,分別創(chuàng)建一個用于回調的宏任務和微任務,無論什么情況呻此,微任務都早于宏任務執(zhí)行

Promise

單線程架構決定了 web 頁面的異步回調轮纫,而多次回調會導致代碼的邏輯不連貫、不線性焚鲜。Promise 封裝異步代碼掌唾,讓處理流程變得線性

64.png

我們來看一下 Promise 的常用用法

let b = function(resolve, reject) {
  let xhr = new XMLHttpRequest()
  xhr.open('get', 'url', true)
  xhr.ontimeout = function(e) {
    reject(e)
  }

  xhr.onreadystatechange = function() {
    resolve(this.responseText, this)
  }
}
let a = new Promise(b)

a.then(res => {}).catch(e => {})

觀察上面代碼,可以發(fā)現:

  • 構建 Promise 對象時忿磅,需要傳入一個 b 函數糯彬,業(yè)務流程都在 b 函數中執(zhí)行
  • 如果運行 b 函數中的業(yè)務執(zhí)行成功了,會調用 resolve 函數葱她;如果執(zhí)行失敗撩扒,則調用 reject 函數
  • 在 b 函數中調用 resolve 函數時,會觸發(fā) Promise.then 中設置的回調函數;調用 reject 函數時吨些,會觸發(fā) Promise.catch 設置的回調函數

首先搓谆,Promise 實現了回調函數的延時綁定
其在代碼上的提現就是先創(chuàng)建了 Promise,執(zhí)行業(yè)務邏輯豪墅;再使用 then 來設置回調函數泉手。resolve 函數會觸發(fā)設置的回調函數

function b(resolve, reject) {
  resolve(100)
}
let a = new Promise(b)

function onResolve(value) {
  console.log(value)
}

a.then(onResolve)

其次,需要將回調函數 onResolve 的返回值穿透到最外層

function resolve(value) {
  let a2 = new Promise((resolve, reject) => {
    resolve(value)
  })
  return a2 //then的返回值也是一個promise
}

這樣就可以實現透傳:a.then().then(value);實現透傳之后但校,Promise 對象的錯誤就具有冒泡性質螃诅,會一直向后傳遞,知道被 catch

我們來實現一個簡易版 Promise

const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'

function MyPromise(fn) {
  this.state = PENDING
  this.value = null
  this.resolveCallbacks = []
  this.rejectCallbacks = []
  const that = this

  function resolve() {
    //   這個地方實際用的是微任務状囱,目的是讓then中的回調先執(zhí)行
    setTimeout(function() {
      if (that.state === PENDING) {
        that.resolveCallbacks.forEach(item => {
          item(that.value)
        })
      }
    }, 0)
  }

  function reject() {
    同上
  }

  try {
    fn(resolve, reject)
  } catch (e) {}
}

// 簡易版

MyPromise.prototype.then = function(cb) {
  const that = this
  //   簡易的透傳术裸,cb不存在時
  cb = typeof cb === 'function' ? cb : v => v
  if (that.state === PENDING) {
    that.resolvedCallbacks.push(cb)
  }
  if (that.state === RESOLVED) {
    cb(that.value)
  }
}
// then函數返回Promise
MyPromise.prototype.then = function(cb) {
  const that = this
  if ((that.state = PENDING)) {
    return new MyPromise((resolve, reject) => {
      that.resolvedCallbacks.push(() => {
        const x = cb(that.value) //此時執(zhí)行了then1的回調
        resolve(x) //返回上一步的返回值
      })
    })
  }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市亭枷,隨后出現的幾起案子袭艺,更是在濱河造成了極大的恐慌,老刑警劉巖叨粘,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猾编,死亡現場離奇詭異,居然都是意外死亡升敲,警方通過查閱死者的電腦和手機答倡,發(fā)現死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門瘪撇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來倔既,“玉大人,你說我怎么就攤上這事佩谣∈蹬睿” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵瓣履,是天一觀的道長练俐。 經常有香客問我,道長腺晾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任归形,我火速辦了婚禮暇榴,結果婚禮上蕉世,老公的妹妹穿的比我還像新娘。我一直安慰自己奸例,他們只是感情好向楼,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著逻卖,像睡著了一般昭抒。 火紅的嫁衣襯著肌膚如雪虚茶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天诈乒,我揣著相機與錄音婆芦,去河邊找鬼。 笑死消约,一個胖子當著我的面吹牛,可吹牛的內容都是我干的导饲。 我是一名探鬼主播氯材,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼氢哮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了冗尤?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤皆看,失蹤者是張志新(化名)和其女友劉穎悬蔽,沒想到半個月后捺宗,有當地人在樹林里發(fā)現了一具尸體泪酱,經...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡倍啥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了始藕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡江耀,死狀恐怖诉植,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情舌稀,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布壁查,位于F島的核電站睡腿,受9級特大地震影響领斥,放射性物質發(fā)生泄漏。R本人自食惡果不足惜月洛,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一嚼黔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧疫赎,春花似錦、人聲如沸捧搞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽晚树。三九已至雅采,卻和暖如春慨亲,著一層夾襖步出監(jiān)牢的瞬間宝鼓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工铐望, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留茂附,地道東北人营曼。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓愚隧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親录煤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內容