参考:https://blog.csdn.net/weixin_45494667/article/details/107296746

一、简介

  • 同步任务(MainTask):同步任务是指JavaScript按照正常顺序执行的代码,比如:函数调用,数值运算等等,只要是执行后立即能够得到结果的就是同步任务。
  • 宏任务 (MacroTask):setTimeout、setInterval、I/O、UI渲染
  • 微任务 (MicroTask) :Promise、Object.obsever、MutationObsever 用户交互事件(User Interaction Event):点击事件onclick、键盘事件onkeywodn、鼠标事件onmouseover等等

    二、执行顺序

    20200712111427449.png
    具体流程:
  1. 执行完主逻辑中的同步任务
  2. 取出微任务队列(MicroTask Queue)中的任务执行,直到队列被完全清空 取出宏任务队列(MacroTaskQueue)中的一个任务执行。
  3. 取出微任务队列(MicroTask Queue)中的任务执行,直到队列被完全清空 重复 3 和 4,直到宏任务队列(MacroTaskQueue)被清空。

20200712111601374.png
宏任务(MacroTask)和微任务(MicroTask)执行顺序

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>demo1:宏任务(MacroTask)和微任务(MicroTask)执行顺序</title>
  6. </head>
  7. <body>
  8. <script type="text/javascript">
  9. console.log('同步任务1 start');
  10. setTimeout(function () {
  11. console.log('宏任务1:setTimeout...');
  12. }, 0);
  13. Promise.resolve().then(function () {
  14. console.log('微任务1 Promise.then() 1')
  15. }).then(function () {
  16. console.log('微任务2 Promise.then() 2')
  17. });
  18. setTimeout(function () {
  19. console.log('宏任务2:setTimeout...');
  20. Promise.resolve().then(function () {
  21. console.log('宏任务2:setTimeout => 微任务 Promise.then()')
  22. });
  23. }, 0);
  24. setTimeout(function () {
  25. console.log('宏任务3:setTimeout...');
  26. }, 0);
  27. Promise.resolve().then(function () {
  28. console.log('微任务3 Promise.then() 1')
  29. }).then(function () {
  30. console.log('微任务3 Promise.then() 2')
  31. })
  32. console.log('同步任务2 end');
  33. </script>
  34. </body>
  35. </html>

三、promise

Promise 的回调函数属于异步任务,会在同步任务之后执行。

new Promise(function (resolve, reject) {
  resolve(1);
}).then(console.log);

console.log(2);
// 2
// 1
上面代码会先输出2,再输出1。因为console.log(2)是同步任务,而then的回调函数属于异步任务,一定晚于同步任务执行。

但是,Promise 的回调函数不是正常的异步任务,而是微任务(microtask)。它们的区别在于,正常任务追加到下一轮事件循环,微任务追加到本轮事件循环。这意味着,微任务的执行时间一定早于正常任务。

setTimeout(function() {
  console.log(1);
}, 0);

new Promise(function (resolve, reject) {
  resolve(2);
}).then(console.log);

console.log(3);
// 3
// 2
// 1

上面代码的输出结果是321。这说明then的回调函数的执行时间,早于setTimeout(fn, 0)。因为then是本轮事件循环执行,setTimeout(fn, 0)在下一轮事件循环开始时执行。