在Vue
中有一个nextTick
方法,偶然一天,我发现无论代码的顺序如何,nextTick
老是要比setTimeout
先要执行。一样是排队,凭什么你nextTick
就要比我快? javascript
开局一道题,内容全靠编。(在node下运行,答案在文末给出。)html
new Promise((resolve) => {
console.log(1);
process.nextTick(() => {
console.log(2);
});
resolve();
process.nextTick(() => {
console.log(3);
});
console.log(4);
}).then(() => {
console.log(5);
});
setTimeout(() => {
console.log(6);
}, 0);
console.log(7);
复制代码
那么,打印的顺序究竟是什么呢?java
for(var i = 0; i < 5; i++){
setTimeout(function after() {
console.log(i);
}, 0);
}
复制代码
这道题想必你们都见得不少了,答案脱口而出5个5。为何呢? 答:闭包。 为何会产生闭包呢? 答:。。。node
这一切的一切都要从女娲补天开始提及(你咋不从盘古开天开始提及呢?)。segmentfault
简单说明一下:api
因此,即使是setTimeout(fn, 0)
(实际上最小时间间隔是4ms)也是会从下一个事件周期开始执行。promise
上例中,因为after函数
引用了i
而且会在下一个事件周期中被调用,致使了i
的内存没办法被释放,等下个周期再来,哼 生米都煮成稀饭了。i
都被煮成5了。浏览器
关于内存,给你们推荐一篇我曾经翻译的一篇文章JavaScript是如何工做的:内存管理 + 如何处理4个常见的内存泄漏。 对理解闭包也很是有帮助。session
这里我只是简单提了一下事件循环,更多的细节参考文末参考文献。闭包
一个宿主环境只有一个事件循环,但能够有多个任务队列。宏任务队列(macro task)与微任务队列(micro task)就是其中之二。
每次事件循环的时候,会先执行宏任务队列中的任务,而后再执行微任务队列中的任务。那么宏任务与微任务分别有哪些呢?
new Promise((resolve) => {
resolve();
}).then(() => {
console.log(1);
});
setTimeout(() => {
console.log(2);
}, 0);
console.log(3);
复制代码
按照上面的说法,应该打印出 三、二、1啊。但实际上却打印出了三、一、2。原来像process.nextTick和Promise这种微任务,都添加的当前循环的微任务队列之中。因此会比当前循环中的全部宏任务要后执行,会比下个循环中的宏任务要先执行。
process.nextTick(() => {
console.log(1);
});
new Promise((resolve) => {
resolve();
}).then(() => {
console.log(2);
});
process.nextTick(() => {
console.log(3);
});
复制代码
为何我要把这两个同属于微任务的拎出来提一下呢?本身测试一下吧,由于结果大概会出乎你的意料。 why?
还好互联网是强大的。没有什么是百度不到的,若是有,那就google。
“process.nextTick 永远大于 promise.then,缘由其实很简单。。。在Node中,_tickCallback在每一次执行完TaskQueue中的一个任务后被调用,而这个_tickCallback中实质上干了两件事:
Vue
中的nextTick
是宏任务与微任务混合使用,须要手动切换。终于真相大白了。定时器:好吧 我就原谅你比我先吧。
那么开头题的答案是什么呢?仍是本身动手测试一下吧。
纸上得来终觉浅,觉知此事要躬行
咦,小姐?什么小姐?你说的是
我:滚,打错了而已。是小结。
我:什么? 你请客!走啊走啊!
楼主被捕,完。
顺序之争还有一个奇怪的现象。
setImmediate(() => {
console.log(1);
});
setTimeout(() => {
console.log(2);
}, 0);
复制代码
然而你会发现,特么有时候打印一、2,有时候打印二、1。你为何像个女人同样啊。
nodejs官网给出的解释是:
若是没有在一个I/O周期执行,那么其执行顺序是不肯定的。
若是在一个I/O周期执行,setImmediate
老是优先于setTimeout
执行。
const fs = require('fs');
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
});
复制代码
老是:先打印immediate再打印timeout。
参考文献: