宏任務 微任務
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~ 執行順序即是
- js調用棧
- 微任務
- 宏任務
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 交互)就會很流暢,不會被阻塞或拖慢。