名词介绍
stack(栈)先进后出queue(队列)先进先出heap(堆) function a() { b(); } function b() { c(); } function c() { console.log('c') } a(); 有入栈,就要有出栈,当函数c执行完,开始出栈setTimeout执行结束的时候,是不是就应该回到执行栈,进行执行输出呢?
答案:并不是!此时,倒计时结束后的setTimeout的可执行函数,被放入了回调队列,最后,setTimeout的可执行函数,被从回调队列中取出,再次放入了执行栈执行栈任务清空后,才会从回调队列头部取出一个任务
console.log(1); setTimeout(function() { console.log(2); }, 0) console.log(3); 上面是一个最简单的例子,输出结果是1,3,2。这是为什么?上图展示了具体的执行顺序:
console.log(1)被压入执行栈setTimeout在执行栈被识别为异步任务,放入webapis中console.log(3)被压入执行栈,此时setTimeout的可执行代码还在回调队列里等待console.log(3)执行完成后,从回调队列头部取出console.log(2),放入执行栈console.log(2)执行宏任务
主代码块、setTimeout、setInterval微任务
Promise、process.nextTick、then、Object.observe、MutationObserver
Microtask(微任务)虽然是队列,但并不是一个一个放入执行栈,而是当执行栈请空,会执行全部Microtask(微任务)队列中的任务,最后才是取回调队列的第一个Macrotask(宏任务)
Macrotask(宏任务),就是前面介绍过的回调队列callback queue, Microtask(微任务)同样是一个任务队列,这个队列的执行顺序是在清空执行栈之后
console.log(1); setTimeout(() => { console.log(2) }) var p = new Promise((resolve, reject) => { console.log(3) resolve("成功") }) p.then(() => { console.log(4) }) console.log(5)按照event loop的概念,应该是1,3,5,2,4,因为setTimeout和then会被放到回调队列里,然后又是先进先出,所以应该是2先输出,4后输出。但事实输出的顺序是1,3,5,4,2!
这是因为promise的then方法,被认为是在Microtask微任务队列当中
setTimeout(() => { console.log(1) }) Promise.resolve().then(() => { console.log(2); Promise.resolve().then(() => { console.log(3); }); }); Promise.resolve().then(() => { console.log(4); }); //结果为 2,4,3,1上面的执行过程是:
将setTimeout给push进宏任务将then(2)push进微任务将then(4)push进微任务任务队列为空,取出微任务第一个then(2)压入执行栈输出2,将then(3)push进微任务任务队列为空,取出微任务第一个then(4)压入执行栈输出4任务队列为空,取出微任务第一个then(3)压入执行栈输出3任务队列为空,微任务也为空,取出宏任务中的setTimeout(1)输出1上面代码输出start,end,2,3,4,5,1
process.nextTick的概念和then不太一样,process.nextTick是加入到执行栈底部,所以和其他的表现并不一致
附加练习题 参考