宏任务和微任务,同步异步,promis,await执行顺序

本文作为EVENLOOP事件循环的延伸:

执行顺序:

                         ------------循环----------

                         |                                              |

=====微任务==》{宏任务==》微任务==》浏览器渲染}=====>>>>

                                            ^

                                            ||

======监控线程===等待=====有返回进行返回=====>>>>>

宏任务和微任务都是任务队列,只是用来存放东西的队列。

主线程可以执行代码,渲染UI之类的。

主线程空闲的时候就会去轮询看看宏任务队列有没有要执行的任务,没有就过,有就取出来执行。

宏任务执行完会去看微任务队列。没有就过,有就取出来执行。

微任务都是在宏任务执行完,空闲的时候执行。

也可以用下面的图表示

 

 更细化的顺序是: 同步--->微任务 ----> 异步

1. 什么是同步,什么是异步;

同步任务: 在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行下一个任务;

异步任务:不进入主线程,认识进入“任务队列” 的任务,只有“任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行

2. 什么是宏任务,什么微任务;

 查了一下,貌似除了微任务,别的都是宏任务! 

宏任务: 整体的 script  和  setTimeout  等!

而微任务貌似只有 : promise.then() (前端而言) node 好像还有一个---process.nextTick() 

先来看一段代码,很多面试题有这个:

console.log('script start');

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

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');

crome结果是:script start, script end, promise1, promise2, setTimeout,但是各浏览器不一致。

Microsoft Edge, Firefox 40, iOS Safari 及桌面 Safari 8.0.8 在 promise1 和 promise2 之前打印 setTimeout ——尽管这似乎是竞争条件导致的。奇怪的是,Firefox 39 和 Safari 8.0.7 是对的。

要知道为啥是这个顺序,需要了解:事件循环的,tasks宏任务 和 microtasks微任务

宏任务

(macro)task,可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。

浏览器为了能够使得JS内部(macro)task与DOM任务能够有序的执行,会在一个(macro)task执行结束后,在下一个(macro)task 执行开始前,对页面进行重新渲染,流程如下:

(macro)task->渲染->(macro)task->...

宏任务包含:

script(整体代码)
setTimeout
setInterval
I/O
UI交互事件
postMessage
MessageChannel
setImmediate(Node.js 环境)

微任务

microtask,可以理解是在当前 task 执行结束后立即执行的任务。也就是说,在当前task任务后,下一个task之前,在渲染之前。

所以它的响应速度相比setTimeout(setTimeout是task)会更快,因为无需等渲染。也就是说,在某一个macrotask执行完后,就会将在它执行期间产生的所有microtask都执行完毕(在渲染前)。

微任务包含:

Promise.then
Object.observe
MutaionObserver
process.nextTick(Node.js 环境)

运行机制

在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务处理模型是比较复杂的,但关键步骤如下:

  • 执行一个宏任务(栈中没有就从事件队列中获取)
  • 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
  • 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
  • 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
  • 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

如图:

 

总结:

  • tasks 按序执行,浏览器会在 tasks 之间执行渲染。
  • microtasks 按序执行,在下面情况时执行:
    • 在每个回调之后,只要没有其它代码正在运行。
    • 在每个 task 的末尾

async/await

async/await本质上还是基于Promise的一些封装,而Promise是属于微任务的一种。所以在使用await关键字与Promise.then相同。
async函数在await之前的代码都是同步执行的,可以理解为await之前的代码属于new Promise时传入的代码,await之后的所有代码都是在Promise.then中的回调

注意await的区别:

 打印结果为

script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

await之后的代码必须等await语句执行完成后(包括微任务完成),才能执行后面的,也就是说,只有运行完await语句,才把await语句后面的全部代码加入到微任务行列,所以,在遇到await promise时,必须等await promise函数执行完毕才能对await语句后面的全部代码加入到微任务中。

总的来说,执行顺序:

1、根据上下文执行同步数据

2、看到await,await+await后面的句子,同等于.then的回调。等正常的js执行完了以后,再走微任务await后的

4、然后是微任务.then后面的内容

6、宏任务(setimeout)

延伸:https://www.cnblogs.com/jiangyuzhen/p/11064408.html 里面很多面试题

posted @ 2021-04-19 11:16  优前程  阅读(1442)  评论(0编辑  收藏  举报