参考资料
Event Loop
JavaScript是单线程的语言,为了处理一些耗时的任务,会将任务分布到主线程和任务队列中。
一旦主线程的所有任务执行完毕,就会读取任务队列里面的任务。
macrotask vs microtask
任务队列里的任务又分macrotask和microtask,并且单独维护。
如果你需要在同步中进行异步任务,那么使用microtask;否则就使用macrotask。
-
macrotasks: setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering
-
microtasks: process.nextTick, Promises, Object.observe, MutationObserver
执行顺序
- 执行主线程的任务
- 遇到一些非立即执行的任务,分别放到macrotask和microtask队列中。
- 立即执行microtask中所有的任务。
- 开始执行macrotask中的第一个任务,并且可能继续产生新的任务。
- 如果4产生了microtask,会继续执行3。
- 接着4开始执行下一个macrotask任务。
执行示例
例子1 then.then
考察了then.then的执行是有依赖顺序的
new Promise((resolve,reject)=>{
console.log("1")
resolve()
}).then(()=>{
console.log("2")
}).then(()=>{
console.log("3")
})
// 1,2,3
new Promise((resolve,reject)=>{
console.log("1")
resolve()
}).then(()=>{
console.log("2")
return new Promise((resolve,reject)=>{
console.log("3")
resolve()
}).then(()=>{
console.log("4")
}).then(()=>{
console.log("5")
})
}).then(()=>{
console.log("6")
})
// 1,2,3,4,5,6
例子2 then.then + then
主要考察了then.then会被当做the.then,被插入到micro队列的末尾,从而最后执行:
Promise.resolve().then(()=>{
console.log(1);
}).then(()=>{
console.log(2);
});
Promise.resolve().then(()=>{
console.log(3);
})
// 1,3,2
console.log('begin');
setTimeout(() => {
console.log('setTimeout 1');
Promise.resolve()
.then(() => {
console.log('promise 1');
setTimeout(() => {
console.log('setTimeout2');
});
})
.then(() => {
console.log('promise 2');
});
new Promise(resolve => {
console.log('a');
resolve();
}).then(() => {
console.log('b');
});
}, 0);
console.log('end');
// begin, end, setTimeout 1, a, promise 1, b, promise 2, setTimeout2,
例子3 macro.micro > macro
考察了第一个setTimeout执行时,生成了micro(输出7),这时候可以插队到第二个setTimeout(输出3)前。
console.log(1);
setTimeout(() => {
console.log(2);
new Promise((resolve) => {
console.log(6);
resolve();
}).then(() => {
console.log(7);
})
})
setTimeout(() => {
console.log(3);
})
new Promise((resolve)=>{
console.log(4);
resolve();
}).then(()=>{
console.log(5);
})
// 1,4,5,2,6,7,3
同样一个插队的例子。
console.log(1);
setTimeout(() => {
console.log(2);
})
setTimeout(() => {
console.log(3);
})
new Promise((resolve)=>{
console.log(4);
resolve();
}).then(()=>{
console.log(5);
})
// 1 4 5 2 3
各种不讲理插队的例子。
console.log(1);
setTimeout(() => {
console.log(2);
})
setTimeout(() => {
console.log(3);
})
new Promise((resolve)=>{
console.log(4);
resolve();
}).then(()=>{
console.log(5);
new Promise((resolve) => {
console.log(6);
resolve();
}).then(() => {
console.log(7);
})
})
// 1 4 5 6 7 2 3
变着法插队的例子。
console.log(1);
setTimeout(() => {
console.log(2);
new Promise((resolve) => {
console.log(6);
resolve();
}).then(() => {
console.log(7);
})
})
setTimeout(() => {
console.log(3);
})
new Promise((resolve)=>{
console.log(4);
resolve();
}).then(()=>{
console.log(5);
})
// 1 4 5 2 6 7 3
例子4 node.js下的执行
在浏览器环境应该输出1 2 3 4,实际在node.js下是1 2 4 3,可见micro不插队,要等macro都走一遍。
setTimeout(function() {
console.log(1);
new Promise(function(resolve) {
console.log(2);
resolve();
}).then(function() {
console.log(3)
})
});
setTimeout(function() {
console.log(4);
});
// 1 2 4 4
尽管如此,对于如下为什么5不是在10面前,仍旧是不理解?
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
// 7 6 8 2 4 9 11 3 10 5 12
网友评论