JavaScript之JS運行機制和順序

宏任務 微任務

JS運行機制 - EventLoop

JavaScript腳本執行方法時,都會進入調用棧中,其中有同步的方法還有異步的方法。也就是微任務(micro task)和宏任務(macro task) 可以現從一個小demo來看下微任務和宏任務的執行順序

console.log(1);
setTimeout(()=>console.log(2));
console.log(3);

答案顯而易見是 1 3 2,但是明明setTimeout裏沒有設置倒計時時間,也就是應該立即執行的,爲啥他還在後面纔出來呢?這就是js的運行機制了

宏任務&微任務

目前主流瀏覽器支持的宏任務setTimeout、MessageChannel、postMessage、setImmediate

常見的微任務MutationObsever Promise.then

JS是單線程的,瀏覽器支持有微任務、 宏任務、消息隊列、調用棧。see the demo

console.log('begin'); //js腳本執行
setTimeout(()=>console.log('setTimeout')); //setTImeout 回調打印
Promise.resolve().then(function() { // pormise 回調打印
  console.log('promise then1');
}).then(function() {
  console.log('promise then2');
});
console.log('end'); // happy ending

撈個大佬的圖來更好的說明,微任務、js調用棧、宏任務、消息隊列之間的關係

so~ 執行順序即是

  1. js調用棧
  2. 微任務
  3. 宏任務

vue中的$nextTick

$nextTick可以讓操作在頁面渲染後在執行,因爲vue的頁面渲染是異步的,如果執行邏輯的時機不對,數據可能會讀取錯誤。 根據vue源碼,若瀏覽器支持Promise,則採用微任務的處理機制,否則使用宏任務(setTimeout)。根據Eventloop的運行機制,就可以知道微任務是在調用棧執行後才執行的。以下貼出核心代碼,註釋上也寫了用microtask queue的原因。地址https://github.com/vuejs/vue/blob/dev/src/core/util/next-tick.js

// 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 */
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.
  // Technically 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)
  }
}

瀏覽器渲染

瀏覽器分爲兩個引擎,js引擎和渲染引擎,在之前的版本中JS和GUI渲染是同步的,後續支持了異步。

WebWorker

Web Worker 的作用,就是爲 JavaScript 創造多線程環境,允許主線程創建 Worker 線程,將一些任務分配給後者運行。在主線程運行的同時,Worker 線程在後臺運行,兩者互不干擾。等到 Worker 線程完成計算任務,再把結果返回給主線程。這樣的好處是,一些計算密集型或高延遲的任務,被 Worker 線程負擔了,主線程(通常負責 UI 交互)就會很流暢,不會被阻塞或拖慢。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章