事件循环机制和task执行顺序的一些概括(javascript)

码途清韵
• 阅读 2375

事件循环(event loop) :

首先说事件队列(task queue)

事件队列是一个存储着待执行任务的队列,其中的任务严格按照时间先后顺序执行,排在队头的任务将会率先执行,而排在队尾的任务会最后执行。
事件队列每次仅执行一个任务,在该任务执行完毕之后,再执行下一个任务。
执行栈则是一个类似于函数调用栈的运行容器,当执行栈为空时,JS 引擎便检查事件队列,如果不为空的话,事件队列便将第一个任务压入执行栈中运行。
当我们设置一个延迟函数的时候,当前脚本并不会阻塞,它只是会在浏览器的事件表中进行记录,程序会继续向下执行。 当延迟的时间结束之后,事件表会将回调函数添加至事件队列中,事件队列拿到了任务过后便将任务压入执行栈(stack)当中,执行栈执行任务

  • 事件循环机制:
检查事件队列是否为空,如果为空,则继续检查;如不为空,则执行 2;
取出事件队列的首部,压入执行栈;
执行任务
检查执行栈,如果执行栈为空,则跳回第 1 步;如不为空,则继续检查;

结合 Web APIs 事件循环:

  1. ajax 请求挂起,然后继续执行后面的代码,至于请求何时响应,对我们的程序不会有影响,甚至它可能永远也不响应,也不会使浏览器阻塞。
    而当响应成功了以后,浏览器的事件表则会将回调函数添加至事件队列中等待执行。
  2. 事件监听器的回调函数也是一个任务,当我们注册了一个事件监听器时,浏览器事件表会进行登记,当我们触发事件时,事件表便将回调函数添加至事件队列当中。
    事件循环器会不停的检查事件队列,如果不为空,则取出队首压入执行栈执行。当一个任务执行完毕之后,事件循环器又会继续不停的检查事件队列,

不过在这间,任务结束后浏览器会对页面进行渲染。这就保证了用户在浏览页面的时候不会出现页面阻塞的情况,这也使 JS 动画成为可能,同步就没动画的渐变效果了。

总结:

  • 事件队列严格按照时间先后顺序将任务压入执行栈执行;
  • 当执行栈为空时,浏览器会一直不停的检查事件队列,如果不为空,则取出第一个任务;
  • 在每一个任务结束之后,浏览器会对页面进行渲染;

- 接下来说一下不同的任务(task)之间的执行顺序问题

microtask

每一个 事件循环(event loop)都有着众多不同的任务来源(task source),这些来源能够保证其中的任务能够有序的执行。不过,在每一轮事件循环结束之后,浏览器可以自行选择将哪一个来源当中的任务加入到执行队列当中

任务Task是严格按照时间顺序 压栈和执行 的,当一个 task 执行结束后,在下一个 task 执行开始前,浏览器可以对页面进行重新渲染

  • Microtask 通常来说就是需要在当前 task 执行结束后立即执行的任务,
例如需要对一系列的任务做出回应,或者是需要异步的执行任务而又不需要分配一个新的 task,这样便可以减小一点性能的开销。microtask
任务队列是一个与 task 任务队列相互独立的队列, microtask 任务将会在每一个 task 任务执行结束之后执行。每一个
task 中产生的 microtask 都将会添加到 microtask 队列中, microtask 中产生的 microtask
将会添加至当前队列的尾部,并且 microtask 会按序的处理完队列中的所有任务。 microtask 类型的任务目前包括了
MutationObserver 以及 Promise 的回调函数。 microtask 执行在当前 task 结束之后,下一个 task 开始之前。
microtask 执行在当前 task 结束之后,下一个 task 开始之前。

另外补充一下上文说到的 Mutation Observer
使用链接见文章: https://segmentfault.com/a/11...

Mutation Observer API 用来监视 DOM 变动。DOM 的任何变动,比如节点的增减、属性的变动、文本内容的变动,这个 API 都可以得到通知。
它等待所有脚本任务完成后,才会运行(即异步触发方式)。
它把 DOM 变动记录封装成一个数组进行处理,而不是一条条个别处理 DOM 变动。
它既可以观察 DOM 的所有类型变动,也可以指定只观察某一类变动。
observe方法用来启动监听,它接受两个参数。
第一个参数:所要观察的 DOM 节点
第二个参数:一个配置对象,指定所要观察的特定变动



总结:
  

  • microtask 和 task 一样严格按照时间先后顺序执行。   
  • microtask 类型的任务包括 Promise callback和 Mutation callback。
  • 当 JS 执行栈为空时,便生成一个 microtask 检查点。

最后 概括一些分类

macrotask:  
script (整体代码),setTimeout, setInterval, setImmediate, I/O, UI rendering. 
microtask:  
process.nextTick, Promise(原生),Object.observe,MutationObserver

在microtask中  process.nextTick 优先级高于 Promise。 

对于任务名称的事件队列关系

task queue == macrotask queue != microtask queue
点赞
收藏
评论区
推荐文章
菜园前端 菜园前端
2年前
什么是JavaScript异步模式
什么是异步模式?不会等待当前任务执行完毕,才会去执行下一个任务,这就是异步模式(Asynchronous)。开启异步后,就会跳过本任务,开始执行下一个任务,后续的逻辑一般会通过回调函数的方式定义。异步模式执行中,涉及到调用栈(Callstack)、消息队列
菜园前端 菜园前端
2年前
什么是 JavaScript 单线程?
原文链接:什么是JavaScript单线程?单线程是指当存在多个任务时候,所有任务都必须排队并且按照顺序执行。这里就会有同学问为什么不是多线程?假设是多线程,同时执行一些任务,某个任务进行了该dom的修改,而某个任务进行了该dom的删除,这样就会出现一些问
菜园前端 菜园前端
2年前
什么是宏任务与微任务?
原文链接:事件循环机制在事件循环中,每进行一次循环操作称为tick,每一次tick的任务处理是比较复杂的。关键步骤如下:1.执行一个宏任务2.执行过程中如果遇到微任务,就将它添加到微任务的任务队列中3.宏任务执行完毕后,立即执行当前微任务队列中的所有微任务
Wesley13 Wesley13
3年前
Java并发基础05. 传统线程同步通信技术
先看一个问题:有两个线程,子线程先执行10次,然后主线程执行5次,然后再切换到子线程执行10,再主线程执行5次……如此往返执行50次。看完这个问题,很明显要用到线程间的通信了,先分析一下思路:首先肯定要有两个线程,然后每个线程中肯定有个50次的循环,因为每个线程都要往返执行任务50次,主线程的任务是执行5次,子线程的任务是执行10次。线程间通信
Stella981 Stella981
3年前
JavaScript:再谈Tasks和Microtasks
JavaScript是单线程,也就是说JS的堆栈中只允许有一类任务在执行,不可以同时执行多类任务。在读js文件时,所有的同步任务是一条task,当然了,每一条task都是一个队列,按顺序执行。而如果在中途遇到了setTimeout这种异步任务,就会将它挂起,放到任务队列中去执行,等执行完毕后,如果有callback,就把callback推入到Tasks中去,
Stella981 Stella981
3年前
Linux的定时任务
任务计划的条件:1.在未来的某个时间点执行一次某个任务(atbatch)2.周期性的执行某个任务(cron)at在指定时间执行任务_用法_at\选项参数\\时间\_选项参数_\l      查看作业\c      显示即将执行任务的细节\d      使用任务id号
Wesley13 Wesley13
3年前
JS异步的底层原理:单线程加事件队列
异步的底层原理:单线程事件队列。js的代码执行时单线程的,所谓单线程:就是js代码时从上到下按顺序依次执行的,一次只能做一件事情。事件队列可以看作一个容器,这个容器存储着js的回调函数,只有js代码执行结束后,才会去事件队列中调用这些回调函数。例:1//以下的代码先执行for循环,再输出sum值,然后输出正常代码执行,最后
Wesley13 Wesley13
3年前
Java多线程之任务执行
Java多线程之任务执行一、在线程中执行任务1.串行的执行任务在应用程序中可以通过多种策略来调度任务,而其中的策略能够更好的利用潜在的并发性。_最简单的策略就是在单个线程中串行的执行各项任务。_public class SingleThreadWebServer {
Stella981 Stella981
3年前
Linux 定时任务调度(crontab命令)
1.crond是Linux下用周期性的执行某种任务或者等待处理某些事件的一个守护进程,crond进程会每分钟定期检查是否有要执行的任务,如果有要执行的任务则自动执行该任务2.Linux下的任务调度1.系统任务调度:系统周期性所要执行的工作,如:写缓存数据到硬盘、清理日志等。系统任务调度的配置文件/etc/c
Stella981 Stella981
3年前
RabbitMQ指南之二:工作队列(Work Queues)
在上一章的指南中,我们写了一个命名队列:生产者往该命名队列发送消息、消费从从该命名队列中消费消息。在本章中,我们将创建一个工作队列,用于在多个工作者之间分配耗时的任务。工作队列(即任务队列)的主要思想是避免立即执行那些需要等他们执行完成的资源密集型任务。相反,我们将任务安排在稍后完成。我们将任务封装为消息并将其发送到队列,后台运行的工作进程将取出任务并执行完
Stella981 Stella981
3年前
JFinal Quartz 支持配置文件和持久化
    随着需求的增加,现在要定时启动一个调度和计划任务,原先写的QuartzPlugin,是持久化保存到数据库中的,从数据库中读取任务并执行。要是添加一个每天循环任务,就要在代码里写一次开始任务的代码,执行后,再注释掉,最后重启项目。否则会因为启动同name,同group的任务而报错org.quartz.ObjectAlreadyExistsE
码途清韵
码途清韵
Lv1
春分至情花开,不负春光不负你。
文章
3
粉丝
0
获赞
0