<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試必考:真的理解 $nextTick 么

          共 11719字,需瀏覽 24分鐘

           ·

          2020-09-02 12:27

          作者:子奕,原文鏈接https://juejin.im/post/5cd9854b5188252035420a13。

          為什么是nextTick

          這里猜測(cè)一下為什么Vue有一個(gè)API叫nextTick

          瀏覽器

          瀏覽器(多進(jìn)程)包含了「Browser進(jìn)程」(瀏覽器的主進(jìn)程)、「第三方插件進(jìn)程」「GPU進(jìn)程」(瀏覽器渲染進(jìn)程),其中「GPU進(jìn)程」(多線程)和Web前端密切相關(guān),包含以下線程:

          • 「GUI渲染線程」
          • 「JS引擎線程」
          • 「事件觸發(fā)線程」(和EventLoop密切相關(guān))
          • 「定時(shí)觸發(fā)器線程」
          • 「異步HTTP請(qǐng)求線程」

          「GUI渲染線程」「JS引擎線程」是互斥的,為了防止DOM渲染的不一致性,其中一個(gè)線程執(zhí)行時(shí)另一個(gè)線程會(huì)被掛起。

          這些線程中,和Vue的nextTick息息相關(guān)的是「JS引擎線程」「事件觸發(fā)線程」

          JS引擎線程和事件觸發(fā)線程

          瀏覽器頁面初次渲染完畢后,「JS引擎線程」結(jié)合「事件觸發(fā)線程」的工作流程如下:

          (1)同步任務(wù)在「JS引擎線程」(主線程)上執(zhí)行,形成「執(zhí)行棧」(Execution Context Stack)。

          (2)主線程之外,「事件觸發(fā)線程」管理著一個(gè)「任務(wù)隊(duì)列」(Task Queue)。只要異步任務(wù)有了運(yùn)行結(jié)果,就在「任務(wù)隊(duì)列」之中放置一個(gè)事件。

          (3)「執(zhí)行棧」中的同步任務(wù)執(zhí)行完畢,系統(tǒng)就會(huì)讀取「任務(wù)隊(duì)列」,如果有異步任務(wù)需要執(zhí)行,將其加到主線程的「執(zhí)行棧」并執(zhí)行相應(yīng)的異步任務(wù)。

          主線程的執(zhí)行流程如下圖所示:

          這里可能是不夠嚴(yán)謹(jǐn)?shù)模诒疚闹?strong style="color: rgb(51, 51, 51);">「事件隊(duì)列」和「任務(wù)隊(duì)列」指向同一個(gè)概念。

          事件循環(huán)機(jī)制(Event Loop)

          「事件觸發(fā)線程」管理的「任務(wù)隊(duì)列」是如何產(chǎn)生的呢?事實(shí)上這些任務(wù)就是從「JS引擎線程」本身產(chǎn)生的,主線程在運(yùn)行時(shí)會(huì)產(chǎn)生「執(zhí)行棧」,棧中的代碼調(diào)用某些異步API時(shí)會(huì)在「任務(wù)隊(duì)列」中添加事件,棧中的代碼執(zhí)行完畢后,就會(huì)讀取「任務(wù)隊(duì)列」中的事件,去執(zhí)行事件對(duì)應(yīng)的回調(diào)函數(shù),如此循環(huán)往復(fù),形成事件循環(huán)機(jī)制,如下圖所示:

          任務(wù)類型

          JS中有兩種任務(wù)類型:「微任務(wù)」(microtask)和「宏任務(wù)」(macrotask),在ES6中,microtask稱為 jobs,macrotask稱為 task。

          「宏任務(wù)」:script (主代碼塊)、setTimeoutsetIntervalsetImmediate 、I/O 、UI rendering

          「微任務(wù)」process.nextTick(Nodejs) 、promiseObject.observeMutationObserver

          這里要重點(diǎn)說明一下,「宏任務(wù)」并非全是異步任務(wù),主代碼塊就是屬于「宏任務(wù)」的一種(「Promises/A+規(guī)范」)。

          它們之間區(qū)別如下:

          • 「宏任務(wù)」是每次「執(zhí)行棧」執(zhí)行的代碼(包括每次從事件隊(duì)列中獲取一個(gè)事件回調(diào)并放到執(zhí)行棧中執(zhí)行)
          • 瀏覽器為了能夠使得「JS引擎線程」「GUI渲染線程」有序切換,會(huì)在當(dāng)前「宏任務(wù)」結(jié)束之后,下一個(gè)「宏任務(wù)」執(zhí)行開始之前,對(duì)頁面進(jìn)行重新渲染(「宏任務(wù)」 > 渲染 ?> 「宏任務(wù)」 > ...)
          • 「微任務(wù)」是在當(dāng)前「宏任務(wù)」執(zhí)行結(jié)束之后立即執(zhí)行的任務(wù)(在當(dāng)前 「宏任務(wù)」執(zhí)行之后,UI渲染之前執(zhí)行的任務(wù))。「微任務(wù)」的響應(yīng)速度相比setTimeout(下一個(gè)「宏任務(wù)」)會(huì)更快,因?yàn)闊o需等待UI渲染。
          • 當(dāng)前「宏任務(wù)」執(zhí)行后,會(huì)將在它執(zhí)行期間產(chǎn)生的所有「微任務(wù)」都執(zhí)行一遍。

          自我灌輸一下自己的理解:

          • 「宏任務(wù)」中的事件是由「事件觸發(fā)線程」來維護(hù)的
          • 「微任務(wù)」中的所有任務(wù)是由「JS引擎線程」維護(hù)的(這只是自我猜測(cè),因?yàn)?strong style="color: rgb(51, 51, 51);">「宏任務(wù)」執(zhí)行完畢后會(huì)立即執(zhí)行「微任務(wù)」,為了提升性能,這種無縫連接的操作放在「事件觸發(fā)線程」來維護(hù)明顯是不合理的)。

          根據(jù)事件循環(huán)機(jī)制,重新梳理一下流程:

          • 執(zhí)行一個(gè)「宏任務(wù)」(首次執(zhí)行的主代碼塊或者「任務(wù)隊(duì)列」中的回調(diào)函數(shù))
          • 執(zhí)行過程中如果遇到「微任務(wù)」,就將它添加到「微任務(wù)」的任務(wù)隊(duì)列中
          • 「宏任務(wù)」執(zhí)行完畢后,立即執(zhí)行當(dāng)前「微任務(wù)」隊(duì)列中的所有任務(wù)(依次執(zhí)行)
          • 「JS引擎線程」掛起,「GUI線程」執(zhí)行渲染
          • 「GUI線程」渲染完畢后掛起,「JS引擎線程」執(zhí)行「任務(wù)隊(duì)列」中的下一個(gè)「宏任務(wù)」

          舉個(gè)栗子,以下示例無法直觀的表述「UI渲染線程」的接管過程,只是表述了「JS引擎線程」的執(zhí)行流程:


          <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>Documenttitle>

          ??<style>
          ????.outer?{
          ??????height:?200px;
          ??????background-color:?red;
          ??????padding:?10px;
          ????}

          ????.inner?{
          ??????height:?100px;
          ??????background-color:?blue;
          ??????margin-top:?50px;
          ????}
          ??
          style>
          head>
          <body>
          ??<div?class="outer">
          ????<div?class="inner">div>
          ??div>
          body>

          <script>
          let?inner?=?document.querySelector('.inner')
          let?outer?=?document.querySelector('.outer')

          //?監(jiān)聽outer元素的attribute變化
          new?MutationObserver(function()?{
          ??console.log('mutate')
          }).observe(outer,?{
          ??attributes:?true
          })

          //?click監(jiān)聽事件
          function?onClick()?{
          ??console.log('click')

          ??setTimeout(function()?{
          ????console.log('timeout')
          ??},?0)

          ??Promise.resolve().then(function()?{
          ????console.log('promise')
          ??})

          ??outer.setAttribute('data-random',?Math.random())
          }

          inner.addEventListener('click',?onClick)

          script>
          html>

          點(diǎn)擊inner元素打印的順序是:「建議放入瀏覽器驗(yàn)證。」

          觸發(fā)的click事件會(huì)加入「宏任務(wù)」隊(duì)列,MutationObserverPromise的回調(diào)會(huì)加入「微任務(wù)」隊(duì)列,setTimeout加入到「宏任務(wù)」隊(duì)列,對(duì)應(yīng)的任務(wù)用對(duì)象直觀的表述一下(自我認(rèn)知的一種表述,只有參考價(jià)值):

          {
          ?//?tasks是宏任務(wù)隊(duì)列
          ??tasks:?[{
          ?script:?'主代碼塊'
          ??},?{
          ????script:?'click回調(diào)函數(shù)',
          ???//?microtasks是微任務(wù)隊(duì)列
          ????microtasks:?[{?
          ??????script:?'Promise'
          ????},?{
          ??????script:?'MutationObserver'
          ????}]
          ??},?{
          ????script:?'setTimeout'
          ??}]
          }

          稍微增加一下代碼的復(fù)雜度,在原有的基礎(chǔ)上給outer元素新增一個(gè)click監(jiān)聽事件:

          outer.addEventListener('click',?onClick)

          點(diǎn)擊inner元素打印的順序是:「建議放入瀏覽器驗(yàn)證。」

          由于冒泡,click函數(shù)再一次執(zhí)行了,對(duì)應(yīng)的任務(wù)用對(duì)象直觀的表述一下(自我認(rèn)知的一種表述,只有參考價(jià)值):

          {
          ??tasks:?[{
          ?script:?'主代碼塊'
          ??},?{
          ????script:?'innter的click回調(diào)函數(shù)',
          ????microtasks:?[{
          ??????script:?'Promise'
          ????},?{
          ??????script:?'MutationObserver'
          ????}]
          ??},?{
          ????script:?'outer的click回調(diào)函數(shù)',
          ????microtasks:?[{
          ??????script:?'Promise'
          ????},?{
          ??????script:?'MutationObserver'
          ????}]
          ??},?{
          ????script:?'setTimeout'
          ??},?{
          ????script:?'setTimeout'
          ??}]
          }

          Node.js中的process.nextTick

          Node.js中有一個(gè)nextTick函數(shù)和Vue中的nextTick命名一致,很容易讓人聯(lián)想到一起(Node.js的Event Loop和瀏覽器的Event Loop有差異)。重點(diǎn)講解一下Node.js中的nextTick的執(zhí)行機(jī)制,簡單的舉個(gè)栗子:

          setTimeout(function()?{
          ??console.log('timeout')
          })

          process.nextTick(function(){
          ??console.log('nextTick?1')
          })

          new?Promise(function(resolve){
          ??console.log('Promise?1')
          ??resolve();
          ??console.log('Promise?2')
          }).then(function(){
          ??console.log('Promise?Resolve')
          })

          process.nextTick(function(){
          ??console.log('nextTick?2')
          })

          在Node環(huán)境(10.3.0版本)中打印的順序:Promise 1 > Promise 2 > nextTick 1 > nextTick 2 > Promise Resolve > timeout

          在Node.js的v10.x版本中對(duì)于process.nextTick的說明如下:

          The process.nextTick() method adds the callback to the "next tick queue". Once the current turn of the event loop turn runs to completion, all callbacks currently in the next tick queue will be called.This is not a simple alias to setTimeout(fn, 0). It is much more efficient. It runs before any additional I/O events (including timers) fire in subsequent ticks of the event loop.

          Vue的API命名nextTick

          Vue官方對(duì)nextTick這個(gè)API的描述:

          在下次 DOM 更新循環(huán)結(jié)束之后執(zhí)行延遲回調(diào)。在修改數(shù)據(jù)之后立即使用這個(gè)方法,獲取更新后的 DOM。

          //?修改數(shù)據(jù)
          vm.msg?=?'Hello'
          //?DOM?還沒有更新
          Vue.nextTick(function?()?{
          ??//?DOM?更新了
          })

          //?作為一個(gè)?Promise?使用?(2.1.0?起新增,詳見接下來的提示)
          Vue.nextTick()
          ?.then(function?()?{
          ??//?DOM?更新了
          })

          2.1.0 起新增:如果沒有提供回調(diào)且在支持 Promise 的環(huán)境中,則返回一個(gè) Promise。請(qǐng)注意 Vue 不自帶 Promise 的 polyfill,所以如果你的目標(biāo)瀏覽器不原生支持 Promise (IE:你們都看我干嘛),你得自己提供 polyfill。0

          可能你還沒有注意到,Vue 異步執(zhí)行 DOM 更新。只要觀察到數(shù)據(jù)變化,Vue 將開啟一個(gè)隊(duì)列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)改變。如果同一個(gè) watcher 被多次觸發(fā),只會(huì)被推入到隊(duì)列中一次。這種在緩沖時(shí)去除重復(fù)數(shù)據(jù)對(duì)于避免不必要的計(jì)算和 DOM 操作上非常重要。然后,在下一個(gè)的事件循環(huán)“tick”中,Vue 刷新隊(duì)列并執(zhí)行實(shí)際 (已去重的) 工作。Vue 在內(nèi)部嘗試對(duì)異步隊(duì)列使用原生的 Promise.then 和 MessageChannel,如果執(zhí)行環(huán)境不支持,會(huì)采用 setTimeout(fn, 0) 代替。

          例如,當(dāng)你設(shè)置 vm.someData = 'new value' ,該組件不會(huì)立即重新渲染。當(dāng)刷新隊(duì)列時(shí),組件會(huì)在事件循環(huán)隊(duì)列清空時(shí)的下一個(gè)“tick”更新。多數(shù)情況我們不需要關(guān)心這個(gè)過程,但是如果你想在 DOM 狀態(tài)更新后做點(diǎn)什么,這就可能會(huì)有些棘手。雖然 Vue.js 通常鼓勵(lì)開發(fā)人員沿著“數(shù)據(jù)驅(qū)動(dòng)”的方式思考,避免直接接觸 DOM,但是有時(shí)我們確實(shí)要這么做。為了在數(shù)據(jù)變化之后等待 Vue 完成更新 DOM ,可以在數(shù)據(jù)變化之后立即使用 Vue.nextTick(callback) 。這樣回調(diào)函數(shù)在 DOM 更新完成后就會(huì)調(diào)用。

          Vue對(duì)于這個(gè)API的感情是曲折的,在2.4版本、2.5版本和2.6版本中對(duì)于nextTick進(jìn)行反復(fù)變動(dòng),原因是瀏覽器對(duì)于「微任務(wù)」的不兼容性影響、「微任務(wù)」「宏任務(wù)」各自優(yōu)缺點(diǎn)的權(quán)衡。

          看以上流程圖,如果Vue使用setTimeout「宏任務(wù)」函數(shù),那么勢(shì)必要等待UI渲染完成后的下一個(gè)「宏任務(wù)」執(zhí)行,而如果Vue使用「微任務(wù)」函數(shù),無需等待UI渲染完成才進(jìn)行nextTick的回調(diào)函數(shù)操作,可以想象在「JS引擎線程」「GUI渲染線程」之間來回切換,以及等待「GUI渲染線程」的過程中,瀏覽器勢(shì)必要消耗性能,這是一個(gè)嚴(yán)謹(jǐn)?shù)目蚣芡耆枰紤]的事情。

          當(dāng)然這里所說的只是nextTick執(zhí)行用戶回調(diào)之后的性能情況考慮,這中間當(dāng)然不能忽略flushBatcherQueue更新Dom的操作,使用異步函數(shù)的另外一個(gè)作用當(dāng)然是要確保同步代碼執(zhí)行完畢Dom更新性能優(yōu)化(例如同步操作對(duì)響應(yīng)式數(shù)據(jù)使用for循環(huán)更新一千次,那么這里只有一次DOM更新而不是一千次)。

          到了這里,對(duì)于Vue中nextTick函數(shù)的命名應(yīng)該是了然于心了,當(dāng)然這個(gè)命名不知道和Node.js的process.nextTick還有沒有什么必然聯(lián)系。

          Vue中NextTick源碼(這里加了一些簡單的注釋說明)

          2.5版本

          /*?@flow?*/
          /*?globals?MessageChannel?*/

          import?{?noop?}?from?'shared/util'
          import?{?handleError?}?from?'./error'
          import?{?isIOS,?isNative?}?from?'./env'

          const?callbacks?=?[]
          let?pending?=?false

          function?flushCallbacks?()?{
          ??pending?=?false
          ??const?copies?=?callbacks.slice(0)
          ??callbacks.length?=?0
          ??for?(let?i?=?0;?i?????copies[i]()
          ??}
          }

          //?在2.4中使用了microtasks?,但是還是存在問題,
          //?在2.5版本中組合使用macrotasks和microtasks,組合使用的方式是對(duì)外暴露withMacroTask函數(shù)
          //?Here?we?have?async?deferring?wrappers?using?both?microtasks?and?(macro)?tasks.
          //?In?
          //?microtasks?have?too?high?a?priority?and?fire?in?between?supposedly
          //?sequential?events?(e.g.?#4521,?#6690)?or?even?between?bubbling?of?the?same
          //?event?(#6566).?However,?using?(macro)?tasks?everywhere?also?has?subtle?problems
          //?when?state?is?changed?right?before?repaint?(e.g.?#6813,?out-in?transitions).
          //?Here?we?use?microtask?by?default,?but?expose?a?way?to?force?(macro)?task?when
          //?needed?(e.g.?in?event?handlers?attached?by?v-on).

          //?2.5版本在nextTick中對(duì)于調(diào)用microtask(微任務(wù))還是macrotask(宏任務(wù))聲明了兩個(gè)不同的變量
          let?microTimerFunc
          let?macroTimerFunc

          //?默認(rèn)使用microtask(微任務(wù))
          let?useMacroTask?=?false


          //?這里主要定義macrotask(宏任務(wù))函數(shù)
          //?macrotask(宏任務(wù))的執(zhí)行優(yōu)先級(jí)
          //?setImmediate?->?MessageChannel?->?setTimeout
          //?setImmediate是最理想的選擇
          //?最Low的狀況是降級(jí)執(zhí)行setTimeout

          //?Determine?(macro)?task?defer?implementation.
          //?Technically?setImmediate?should?be?the?ideal?choice,?but?it's?only?available
          //?in?IE.?The?only?polyfill?that?consistently?queues?the?callback?after?all?DOM
          //?events?triggered?in?the?same?loop?is?by?using?MessageChannel.
          /*?istanbul?ignore?if?*/
          if?(typeof?setImmediate?!==?'undefined'?&&?isNative(setImmediate))?{
          ??macroTimerFunc?=?()?=>?{
          ????setImmediate(flushCallbacks)
          ??}
          }?else?if?(typeof?MessageChannel?!==?'undefined'?&&?(
          ??isNative(MessageChannel)?||
          ??//?PhantomJS
          ??MessageChannel.toString()?===?'[object?MessageChannelConstructor]'
          ))?{
          ??const?channel?=?new?MessageChannel()
          ??const?port?=?channel.port2
          ??channel.port1.onmessage?=?flushCallbacks
          ??macroTimerFunc?=?()?=>?{
          ????port.postMessage(1)
          ??}
          }?else?{
          ??/*?istanbul?ignore?next?*/
          ??macroTimerFunc?=?()?=>?{
          ????setTimeout(flushCallbacks,?0)
          ??}
          }


          //?這里主要定義microtask(微任務(wù))函數(shù)
          //?microtask(微任務(wù))的執(zhí)行優(yōu)先級(jí)
          //?Promise?->?macroTimerFunc
          //?如果原生不支持Promise,那么執(zhí)行macrotask(宏任務(wù))函數(shù)

          //?Determine?microtask?defer?implementation.
          /*?istanbul?ignore?next,?$flow-disable-line?*/
          if?(typeof?Promise?!==?'undefined'?&&?isNative(Promise))?{
          ??const?p?=?Promise.resolve()
          ??microTimerFunc?=?()?=>?{
          ????p.then(flushCallbacks)
          ????//?in?problematic?UIWebViews,?Promise.then?doesn't?completely?break,?but
          ????//?it?can?get?stuck?in?a?weird?state?where?callbacks?are?pushed?into?the
          ????//?microtask?queue?but?the?queue?isn't?being?flushed,?until?the?browser
          ????//?needs?to?do?some?other?work,?e.g.?handle?a?timer.?Therefore?we?can
          ????//?"force"?the?microtask?queue?to?be?flushed?by?adding?an?empty?timer.
          ????if?(isIOS)?setTimeout(noop)
          ??}
          }?else?{
          ??//?fallback?to?macro
          ??microTimerFunc?=?macroTimerFunc
          }


          //?對(duì)外暴露withMacroTask?函數(shù)
          //?觸發(fā)變化執(zhí)行nextTick時(shí)強(qiáng)制執(zhí)行macrotask(宏任務(wù))函數(shù)

          /**
          ?*?Wrap?a?function?so?that?if?any?code?inside?triggers?state?change,
          ?*?the?changes?are?queued?using?a?(macro)?task?instead?of?a?microtask.
          ?*/

          export?function?withMacroTask?(fn:?Function):?Function?{
          ??return?fn._withTask?||?(fn._withTask?=?function?()?{
          ????useMacroTask?=?true
          ????try?{
          ??????return?fn.apply(null,?arguments)
          ????}?finally?{
          ??????useMacroTask?=?false????
          ????}
          ??})
          }

          //?這里需要注意pending
          export?function?nextTick?(cb?:?Function,?ctx?:?Object)?{
          ??let?_resolve
          ??callbacks.push(()?=>?{
          ????if?(cb)?{
          ??????try?{
          ????????cb.call(ctx)
          ??????}?catch?(e)?{
          ????????handleError(e,?ctx,?'nextTick')
          ??????}
          ????}?else?if?(_resolve)?{
          ??????_resolve(ctx)
          ????}
          ??})
          ??if?(!pending)?{
          ????pending?=?true
          ????if?(useMacroTask)?{
          ??????macroTimerFunc()
          ????}?else?{
          ??????microTimerFunc()
          ????}
          ??}
          ??//?$flow-disable-line
          ??if?(!cb?&&?typeof?Promise?!==?'undefined')?{
          ????return?new?Promise(resolve?=>?{
          ??????_resolve?=?resolve
          ????})
          ??}
          }

          2.6版本

          /*?@flow?*/
          /*?globals?MutationObserver?*/

          import?{?noop?}?from?'shared/util'
          import?{?handleError?}?from?'./error'
          import?{?isIE,?isIOS,?isNative?}?from?'./env'

          export?let?isUsingMicroTask?=?false

          const?callbacks?=?[]
          let?pending?=?false

          function?flushCallbacks?()?{
          ??pending?=?false
          ??const?copies?=?callbacks.slice(0)
          ??callbacks.length?=?0
          ??for?(let?i?=?0;?i?????copies[i]()
          ??}
          }

          //?在2.5版本中組合使用microtasks 和macrotasks,但是重繪的時(shí)候還是存在一些小問題,而且使用macrotasks在任務(wù)隊(duì)列中會(huì)有幾個(gè)特別奇怪的行為沒辦法避免,So又回到了之前的狀態(tài),在任何地方優(yōu)先使用microtasks 。
          //?Here?we?have?async?deferring?wrappers?using?microtasks.
          //?In?2.5?we?used?(macro)?tasks?(in?combination?with?microtasks).
          //?However,?it?has?subtle?problems?when?state?is?changed?right?before?repaint
          //?(e.g.?#6813,?out-in?transitions).
          //?Also,?using?(macro)?tasks?in?event?handler?would?cause?some?weird?behaviors
          //?that?cannot?be?circumvented?(e.g.?#7109,?#7153,?#7546,?#7834,?#8109).
          //?So?we?now?use?microtasks?everywhere,?again.
          //?A?major?drawback?of?this?tradeoff?is?that?there?are?some?scenarios
          //?where?microtasks?have?too?high?a?priority?and?fire?in?between?supposedly
          //?sequential?events?(e.g.?#4521,?#6690,?which?have?workarounds)
          //?or?even?between?bubbling?of?the?same?event?(#6566).
          let?timerFunc

          //?The?nextTick?behavior?leverages?the?microtask?queue,?which?can?be?accessed
          //?via?either?native?Promise.then?or?MutationObserver.
          //?MutationObserver?has?wider?support,?however?it?is?seriously?bugged?in
          //?UIWebView?in?iOS?>=?9.3.3?when?triggered?in?touch?event?handlers.?It
          //?completely?stops?working?after?triggering?a?few?times...?so,?if?native
          //?Promise?is?available,?we?will?use?it:
          /*?istanbul?ignore?next,?$flow-disable-line?*/


          //?task的執(zhí)行優(yōu)先級(jí)
          //?Promise?->?MutationObserver?->?setImmediate?->?setTimeout

          if?(typeof?Promise?!==?'undefined'?&&?isNative(Promise))?{
          ??const?p?=?Promise.resolve()
          ??timerFunc?=?()?=>?{
          ????p.then(flushCallbacks)
          ????//?In?problematic?UIWebViews,?Promise.then?doesn't?completely?break,?but
          ????//?it?can?get?stuck?in?a?weird?state?where?callbacks?are?pushed?into?the
          ????//?microtask?queue?but?the?queue?isn't?being?flushed,?until?the?browser
          ????//?needs?to?do?some?other?work,?e.g.?handle?a?timer.?Therefore?we?can
          ????//?"force"?the?microtask?queue?to?be?flushed?by?adding?an?empty?timer.
          ????if?(isIOS)?setTimeout(noop)
          ??}
          ??isUsingMicroTask?=?true
          }?else?if?(!isIE?&&?typeof?MutationObserver?!==?'undefined'?&&?(
          ??isNative(MutationObserver)?||
          ??//?PhantomJS?and?iOS?7.x
          ??MutationObserver.toString()?===?'[object?MutationObserverConstructor]'
          ))?{
          ??//?Use?MutationObserver?where?native?Promise?is?not?available,
          ??//?e.g.?PhantomJS,?iOS7,?Android?4.4
          ??//?(#6466?MutationObserver?is?unreliable?in?IE11)
          ??let?counter?=?1
          ??const?observer?=?new?MutationObserver(flushCallbacks)
          ??const?textNode?=?document.createTextNode(String(counter))
          ??observer.observe(textNode,?{
          ????characterData:?true
          ??})
          ??timerFunc?=?()?=>?{
          ????counter?=?(counter?+?1)?%?2
          ????textNode.data?=?String(counter)
          ??}
          ??isUsingMicroTask?=?true
          }?else?if?(typeof?setImmediate?!==?'undefined'?&&?isNative(setImmediate))?{
          ??//?Fallback?to?setImmediate.
          ??//?Techinically?it?leverages?the?(macro)?task?queue,
          ??//?but?it?is?still?a?better?choice?than?setTimeout.
          ??timerFunc?=?()?=>?{
          ????setImmediate(flushCallbacks)
          ??}
          }?else?{
          ??//?Fallback?to?setTimeout.
          ??timerFunc?=?()?=>?{
          ????setTimeout(flushCallbacks,?0)
          ??}
          }

          export?function?nextTick?(cb?:?Function,?ctx?:?Object)?{
          ??let?_resolve
          ??callbacks.push(()?=>?{
          ????if?(cb)?{
          ??????try?{
          ????????cb.call(ctx)
          ??????}?catch?(e)?{
          ????????handleError(e,?ctx,?'nextTick')
          ??????}
          ????}?else?if?(_resolve)?{
          ??????_resolve(ctx)
          ????}
          ??})
          ??if?(!pending)?{
          ????pending?=?true
          ????timerFunc()
          ??}
          ??//?$flow-disable-line
          ??if?(!cb?&&?typeof?Promise?!==?'undefined')?{
          ????return?new?Promise(resolve?=>?{
          ??????_resolve?=?resolve
          ????})
          ??}
          }

          總結(jié)

          本文的表述可能存在一些不嚴(yán)謹(jǐn)?shù)牡胤健?/p>

          ??愛心三連擊

          1.看到這里了就點(diǎn)個(gè)在看支持下吧,你的在看是我創(chuàng)作的動(dòng)力。

          2.關(guān)注公眾號(hào)程序員成長指北,回復(fù)「1」加入Node進(jìn)階交流群!「在這里有好多 Node 開發(fā)者,會(huì)討論 Node 知識(shí),互相學(xué)習(xí)」!

          3.也可添加微信【ikoala520】,一起成長。


          “在看轉(zhuǎn)發(fā)”是最大的支持

          瀏覽 81
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩久久精品 | 高清亚洲视频 | 亚洲一区二区三区蜜桃 | 精品传媒一区二区三区 | 欧美一级内射美妇网站 |