JS为单线程语言,但是其运行顺序却不是如眼所示的代码顺序,而是有一套规则,这套规则就是任务队列,主要内容是宏任务(macrotask)与微任务(microtask)。

代码一示例:

  1. //主线程直接执行
  2. console.log('1');
  3. //丢到宏事件队列中
  4. setTimeout(function() {
  5. console.log('2');
  6. process.nextTick(function() {
  7. console.log('3');
  8. })
  9. new Promise(function(resolve) {
  10. console.log('4');
  11. resolve();
  12. }).then(function() {
  13. console.log('5')
  14. })
  15. })
  16. //微事件1
  17. process.nextTick(function() {
  18. console.log('6');
  19. })
  20. //主线程直接执行
  21. new Promise(function(resolve) {
  22. console.log('7');
  23. resolve();
  24. }).then(function() {
  25. //微事件2
  26. console.log('8')
  27. })
  28. //丢到宏事件队列中
  29. setTimeout(function() {
  30. console.log('9');
  31. process.nextTick(function() {
  32. console.log('10');
  33. })
  34. new Promise(function(resolve) {
  35. console.log('11');
  36. resolve();
  37. }).then(function() {
  38. console.log('12')
  39. })
  40. })

输出顺序:1 7 6 8 2 4 9 11 3 10 5 12

代码二示例:

  1. process.nextTick(() => console.log('nextTick'));
  2. new Promise(resolve => {
  3. console.log('promise1');
  4. resolve();
  5. })
  6. .then(() => console.log('promise2'))
  7. .then(() => console.log('promise3'));
  8. setImmediate(() => console.log('setImmediate'));
  9. setTimeout(() => console.log('timeout'), 0);
  10. console.log('start');

输出顺序:promise1 start nextTick promise2 promise3 timeout setImmediate

如果看懂了以上代码运行结果,就不必向下看了。

一、宏任务与微任务区别?

宏任务比微任务执行优先级要低,如果有微任务,会首先执行所有的微任务。
宏任务代码包含中主代码块、setTimeout、setInterval、setImmediate,且执行顺序setTimeout>setImmediate;微任务包含process.nextTick、Promise、MutationObserver,且执行优先级process.nextTick > Promise > MutationObserver。

二、setTimeout与console.log(‘1’)同作为宏任务,为什么setTimeout执行结果不紧跟console.log(‘1’)的执行结果?

  1. setTimeout被称为宏任务,是指其内部的回调函数作为宏任务。setTimeout延迟执行,将内部的回调函数作为下一轮的宏任务。Promise被称为微任务,是指调用了.then的回调函数部分为微任务,其Promise主体内的console.log('7')依然为本轮的宏任务。另:process.nextTick的回调函数为微任务。

三、整体流程

  1. 在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。<br /> 如下图所示:如setTimeout函数为异步任务,会被加入到EventTable中,当计时结束后,其回调函数①会被加入到EventQueue中;Promise主体的代码会作为同步任务,加入到主线程顺序执行,但其.then回调函数会被加入到EventTable中,当回调函数②被触发,会被加入到EventQueue中。①和②同时在EventQueue中,犹豫②为微任务,所以②会比①优先执行。<br />![image.png](https://cdn.nlark.com/yuque/0/2019/png/641699/1575773175718-2c88dca0-ae45-45ea-b2eb-0f50e65d81f7.png#align=left&display=inline&height=310&name=image.png&originHeight=620&originWidth=633&size=195105&status=done&style=none&width=316.5)