JS前端宏任务微任务及Event Loop使用详解


Posted in Javascript onJuly 23, 2022

前言

首先我们要了解javascript是一个单线程的脚本语言,也就是说我们在执行代码的过程中不会出现同时进行两个进程(执行两段代码)。

Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。

同步:一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去。

异步:进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。

进程:狭义上,就是正在运行的程序的实例。广义上,进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。描述的是CPU在运行指令及加载和保存上下文所需要的时间。

线程:是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位。指运行中的程序的调度单位。

执行栈:V8(谷歌浏览器引擎)内部维护出来的一个用来存放函数的执行上下文环境的一个栈结构。

相信有不少人和我有着一样的疑惑,既然javascript是单线程的脚本语言,那么他有什么优势呢,为什么不写成多进程的呢?

  • 单线程不仅可以节省内存同时也可以节省上下文切换时间;
  • 不会和渲染线程冲突(如果JS是双线程的,当页面渲染线程还在执行时,JS已经将渲染页面的参数修改,就会导致页面渲染出现问题)

宏任务

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

宏任务队列

  • I/O
  • UI-rendering (页面渲染)
  • script
  • setTimeout
  • setInterval
  • setImmediate (node环境下是,而浏览器环境下不是)
  • requestAnimationFrame (在浏览器环境是,而node环境不是)

requestAnimationFrame在MDN的定义为,下次页面重绘前所执行的操作,而重绘也是作为宏任务的一个步骤来存在的,且该步骤晚于微任务的执行。

微任务

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

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

微任务队列

  • process.nextTick (node环境下是,而浏览器环境下不是)
  • promise.then
  • MutationObserver (在浏览器环境是,而node环境不是)

Event-Loop

上面我们讲了半天宏任务、微任务等各种任务的执行那么vent-Loop到底是个啥?

javascript是一个单进程的语言,同一时间不能处理多个任务,所以什么时候执行宏任务什么时候执行微任务呢?于是乎我们需要Event Loop-事件循环机制(计算机系统的一种运作机制)这样一个判断逻辑存在。

同步代码: js为脚本语言,对于同步代码来说,自上而下进行解释执行。

异步代码: 对于一个任务,分为多个片段来进行执行, 先执行一段,如果碰到比较耗时间的操作,比如本地或者网络io请求。 第一段开始进行io之后,把执行权交由其他任务,当io完成后再来执行后半段的任务(io操作比较耗时,在系统进行io时,主线程是空闲的)

执行顺序

1. 首先执行同步代码,这属于宏任务;

2. 当执行完所有的同步代码后,执行栈为空,检查是否有异步代码要执行;

3. 执行微任务;

4. 执行完微任务后,有必要的情况下会渲染页面;

5. 开启下一轮 Event Loop,执行宏任务中的代码;

代码在执行过程中,遇到异步代码,会将异步代码用队列装起来(挂起)

实战出真理,我们用一个?感受一下
console.log('start');          // 1
function foo(){
   console.log('foo');           
}
foo()                          // 2
setTimeout(function() {        //异步代码中的宏任务,先挂起
   console.log('setTimeout'); // 7
},1000)
new Promise(resolve =>{
   console.log('promise');    // 3
   resolve()
})
   .then(function() {        //异步代码中的微任务,先挂起
       console.log('promise1');// 5
   })
   .then(function() {        //异步代码中的微任务,先挂起
       console.log('promise2');// 6
   })
console.log('end');            // 4

我们跟着实行顺序来分析,第一步首先执行同步代码,所以首先打印出来‘start’,调用函数foo打印出来‘foo’,执行Promise函数打印出来‘promise’,最后打印出来‘end’同步代码就执行完毕,再检查是否有异步代码要执行,第三步执行微任务,所以先打印出第一微任务列表中的‘promise1’,紧接着打印第二微任务列表中的‘promise2’纯js不用渲染页面,最后开启下一轮的Event-Loop,执行宏任务中的代码,而setTimeout就是异步代码中的宏任务所以最终打印出来‘setTimeout’,最后我们来看看在谷歌浏览器中的打印效果。

JS前端宏任务微任务及Event Loop使用详解

这里有个小细节,如果将定时器setTimeout的时间设置为0呢?结果其实还是一样的,setTimeout不会因为时间而改变执行顺序,因为它仍然是异步代码中的宏任务,不会因为延迟执行的时间而改变。

结语

以上就是我对于宏任务、微任务以及事件循环机制(Event Loop)的一些浅显的认识与理解,更多关于JS宏任务微任务Event Loop的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
动态创建的表格单元格中的事件实现代码
Dec 30 Javascript
Angularjs中如何使用filterFilter函数过滤
Feb 06 Javascript
jQuery实现的导航动画效果(附demo源码)
Apr 01 Javascript
JS实现拖拽的方法分析
Dec 20 Javascript
详谈js中数组(array)和对象(object)的区别
Feb 27 Javascript
node.js操作mysql简单实例
May 25 Javascript
Vue官网todoMVC示例代码
Jan 29 Javascript
JavaScript动态添加数据到表单并提交的几种方式
Jun 26 Javascript
微信小程序(订阅消息)功能
Oct 25 Javascript
JS如何在不同平台实现多语言方式
Jul 16 Javascript
js+cavans实现图片滑块验证
Sep 29 Javascript
js屏蔽F12审查元素,禁止修改页面代码等实现代码
Oct 02 Javascript
关于对TypeScript泛型参数的默认值理解
Jul 15 #Javascript
vue递归实现树形组件
Jul 15 #Vue.js
VUE递归树形实现多级列表
Jul 15 #Vue.js
javascript进阶篇深拷贝实现的四种方式
Jul 07 #Javascript
js面向对象编程OOP及函数式编程FP区别
Jul 07 #Javascript
类和原型的设计模式之复制与委托差异
JS高级程序设计之class继承重点详解
Jul 07 #Javascript
You might like
自己动手做一个SQL解释器
2006/10/09 PHP
PHP使用HTML5 FileApi实现Ajax上传文件功能示例
2019/07/01 PHP
PHP调用QQ互联接口实现QQ登录网站功能示例
2019/10/24 PHP
Jquery AJAX 用于计算点击率(统计)
2010/06/30 Javascript
火狐4、谷歌12不支持Jquery Validator的解决方法分享
2011/06/20 Javascript
JS 控件事件小结
2012/10/31 Javascript
JS保留两位小数 四舍五入函数的小例子
2013/11/20 Javascript
利用Keydown事件阻止用户输入实现代码
2014/03/11 Javascript
js原型继承的两种方法对比介绍
2014/03/30 Javascript
jQuery中的height innerHeight outerHeight区别示例介绍
2014/06/15 Javascript
node.js+Ajax实现获取HTTP服务器返回数据
2014/11/26 Javascript
jQuery EasyUI datagrid在翻页以后仍能记录被选中行的实现代码
2016/08/15 Javascript
微信小程序中多个页面传参通信的学习与实践
2017/05/05 Javascript
Node.js 实现简单的接口服务器的实例代码
2017/05/23 Javascript
详解webpack 入门总结和实践(按需异步加载,css单独打包,生成多个入口文件)
2017/06/20 Javascript
jquery手机触屏滑动拼音字母城市选择器的实例代码
2017/12/11 jQuery
vue.js移动数组位置,同时更新视图的方法
2018/03/08 Javascript
详解webpack-dev-server使用方法
2018/09/14 Javascript
记一次webapck4 配置文件无效的解决历程
2018/09/19 Javascript
JS/HTML5游戏常用算法之追踪算法实例详解
2018/12/12 Javascript
微信小程序地图(map)组件点击(tap)获取经纬度的方法
2019/01/10 Javascript
js的继承方法小结(prototype、call、apply)(推荐)
2019/04/17 Javascript
node.js使用mongoose操作数据库实现购物车的增、删、改、查功能示例
2019/12/23 Javascript
JavaScript中的this原理及6种常见使用场景详解
2020/02/14 Javascript
Vue项目打包部署到apache服务器的方法步骤
2021/02/01 Vue.js
[06:45]DOTA2卡尔工作室 英雄介绍幻影长矛手篇
2013/07/12 DOTA
Python配置文件解析模块ConfigParser使用实例
2015/04/13 Python
Python实现的读写json文件功能示例
2018/06/05 Python
python判断一个数是否能被另一个整数整除的实例
2018/12/12 Python
Python 隐藏输入密码时屏幕回显的实例
2019/02/19 Python
Windows下Pycharm远程连接虚拟机中Centos下的Python环境(图文教程详解)
2020/03/19 Python
Python 炫技操作之合并字典的七种方法
2020/04/10 Python
Python读取二进制文件代码方法解析
2020/06/22 Python
Java servlet面试题
2012/03/04 面试题
活动宣传策划方案
2014/05/23 职场文书
暑假开始了,你的暑假学习计划写好了吗?
2019/07/04 职场文书