1. eventloop
此图为eventloop的直观图,附上个人理解:
将代码划分成立即执行代码,宏任务代码,微任务代码,后两者分别放入宏队列和微队列当中,立即执行代码执行后执行所有微任务,这算第一轮结束,然后再执行第二个宏任务,如此循环.
例题:
执行结果为:
1 7 6 8 2 4 3 5 9 11 10 12 分为四轮执行
注意:执行顺序
promise>nexttick>then>settimeout
因为
Promise是一个micro task 主线程是一个task micro task queue会在task后面执行
promise.then是一个micro task
setTimeout返回的函数是一个新的task macro task queue
所以Promise以及promise.then会先于新task执行
至于为什么nexttick会比promise.then先执行:
在Node中,_tickCallback在每一次执行完TaskQueue中的一个任务后被调用,而这个_tickCallback中实质上干了两件事:
1.nextTickQueue中所有任务执行掉(长度最大1e4,Node版本v6.9.1)
2.第一步执行完后执行_runMicrotasks函数,执行microtask中的部分(promise.then注册的回调)所以很明显process.nextTick > promise.then”
2.为了能在DOM更新之后操作新元素,必须要对操作行为进行延时(队列控制)
每一次事件循环都包含一个microtask队列,在循环结束后会依次执行队列中的microtask并移除,然后再开始下一次事件循环。
在执行microtask的过程中后加入microtask队列的微任务,也会在下一次事件循环之前被执行。也就是说,macrotask总要等到microtask都执行完后才能执行,microtask有着更高的优先级。
microtask的这一特性,简直是做队列控制的最佳选择啊!vue进行DOM更新内部也是调用nextTick来做异步队列控制。而当我们自己调用nextTick的时候,它就在更新DOM的那个microtask后追加了我们自己的回调函数,从而确保我们的代码在DOM更新后执行,同时也避免了setTimeout可能存在的多次执行问题。
常见的microtask有:Promise、MutationObserver、Object.observe(废弃),以及nodejs中的process.nextTick.
nexttick源码中显示,优先度:promise>MO>settimeout(但会因为兼容问题而降级)
//1. promise.then延迟调用
if (typeof Promise !== 'undefined' && isNative(Promise)) {
var p = Promise.resolve();
var logError = function (err) { console.error(err); };
timerFunc = function () {
p.then(nextTickHandler).catch(logError);
if (isIOS) { setTimeout(noop); }
};
}
//2. MutationObserver 监听变化
else if (typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
var counter = 1;
var observer = new MutationObserver(nextTickHandler);
var textNode = document.createTextNode(String(counter));
observer.observe(textNode, {
characterData: true
});
timerFunc = function () {
counter = (counter + 1) % 2;
textNode.data = String(counter);
};
}
//3.setTimeout延迟器
else {
timerFunc = function () {
setTimeout(nextTickHandler, 0);
};
}
3.总结
nexttick源码前半段是为了延时nexttickdhandle函数,promise,mo,settimeout,nexttickdhandle函数就是执行回调函数nexttick为了解决回调函数在dom渲染之后进行,利用了微任务这个概念(microtask因为其高优先级特性,能确保队列中的微任务在一次事件循环前被执行完毕),即以微任务的形式添加操作任务,这个操作任务会在dom这个微任务执行后完成(因为是后添加的),我们把这个解决方案叫队列控制,然而实现队列控制的最佳选择是promise,所以在nexttick源码体现出来的方式是优先判断是否兼容promsie