实例分析js事件循环机制


Posted in Javascript onDecember 13, 2017

本文通过实例给大家详细分析了JS中事件循环机制的原理和用法,以下是全部内容:

var start = new Date()
setTimeout(function () {
 var end = new Date
 console.log('Time elapsed:', end - start, 'ms')
}, 500)
while (new Date() - start < 1000) {
}

有其他语言能完成预期的功能吗?Java, 在Java.util.Timer中,对于定时任务的解决方案是通过多线程手段实现的,任务对象存储在任务队列,由专门的调度线程,在新的子线程中完成任务的执行

js是单线程的

JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。

为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

函数调用栈和任务队列

实例分析js事件循环机制

调用栈

JS执行时会形成调用栈,调用一个函数时,返回地址、参数、本地变量都会被推入栈中,如果当前正在运行的函数中调用另外一个函数,则该函数相关内容也会被推入栈顶.该函数执行完毕,则会被弹出调用栈.变量也随之弹出,由于复杂类型值存放于堆中,因此弹出的只是指针,他们的值依然在堆中,由GC决定回收.

事件循环(event loop) & 任务队列(task queue)

JavaScript 主线程拥有一个执行栈以及一个任务队列

遇到异步操作(例如:setTimeout, AJAX)时,异步操作会由浏览器(OS)执行,浏览器会在这些任务完成后,将事先定义的回调函数推入主线程的任务队列(task queue)中,当主线程的执行栈清空之后会读取task queue中的回调函数,当task queue被读取完毕之后,主线程接着执行,从而进入一个无限的循环,这就是事件循环.

主线程执行栈 & 任务队列 循环执行,构成事件循环

结论

setTimeout()只是将事件插入了"任务队列",必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。

另一个例子

(function test() {
 setTimeout(function() {console.log(4)}, 0);
 new Promise(function executor(resolve) {
 console.log(1);
 for( var i=0 ; i<10000 ; i++ ) {
 i == 9999 && resolve();
 }
 console.log(2);
 }).then(function() {
 console.log(5);
 });
 console.log(3);
})()

Macrotask & Microtask

macrotask 和 microtask 是异步任务的两种分类。在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。

macro-task: script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering
micro-task: process.nextTick, Promises(这里指浏览器实现的原生 Promise), Object.observe, MutationObserver

实例分析js事件循环机制

结论

全部代码(script) macrotask -> microtask queue (含有promise.then) -> macrotask(setTimeout) -> 下一个microtask

Node.js的事件循环

process.nextTick & setImmediate

process.nextTick指定的任务总是发生在所有异步任务之前

setImmediate指定的任务总是在下一次Event Loop时执行

process.nextTick(function A() {
 console.log(1);
 process.nextTick(function B(){console.log(2);});
});
setTimeout(function timeout() {
 console.log('TIMEOUT FIRED');
}, 0)
new Promise(function(resolve) {
 console.log('glob1_promise');
 resolve();
}).then(function() {
 console.log('glob1_then')
})
process.nextTick(function() {
 console.log('glob1_nextTick');
})
Javascript 相关文章推荐
Javascript打印网页部分内容的脚本
Nov 17 Javascript
jQuery循环滚动展示代码 可应用到文字和图片上
May 11 Javascript
javascript 控制input只允许输入的各种指定内容
Jun 19 Javascript
jQuery中toggleClass()方法用法实例
Jan 05 Javascript
JavaScript弹出新窗口后向父窗口输出内容的方法
Apr 06 Javascript
原生js实现中奖信息无间隙滚动效果
Jan 18 Javascript
JS实现的数字格式化功能示例
Feb 10 Javascript
Omi v1.0.2发布正式支持传递javascript表达式
Mar 21 Javascript
jQueryUI Sortable 应用Demo(分享)
Sep 07 jQuery
JS常见DOM节点操作示例【创建 ,插入,删除,复制,查找】
May 14 Javascript
JS事件绑定的常用方式实例总结
Mar 02 Javascript
Vue向后台传数组数据,springboot接收vue传的数组数据实例
Nov 12 Javascript
javascript实现QQ空间相册展示源码
Dec 12 #Javascript
自定义PC微信扫码登录样式写法
Dec 12 #Javascript
基于模板引擎Jade的应用(详解)
Dec 12 #Javascript
jquery获取transform里的值实现方法
Dec 12 #jQuery
JS排序算法之希尔排序与快速排序实现方法
Dec 12 #Javascript
将Sublime Text 3 添加到右键中的简单方法
Dec 12 #Javascript
详解vue渲染函数render的使用
Dec 12 #Javascript
You might like
phpMyAdmin链接MySql错误 个人解决方案
2009/12/28 PHP
使用PHP获取网络文件的实现代码
2010/01/01 PHP
PHP获取当前页面完整URL的实现代码
2013/06/10 PHP
php文件缓存类用法实例分析
2015/04/22 PHP
php使用Header函数,PHP_AUTH_PW和PHP_AUTH_USER做用户验证
2016/05/04 PHP
Laravel搭建后台登录系统步骤详解
2016/07/26 PHP
基于PHP实现短信验证码发送次数限制
2020/07/11 PHP
javascript的事件描述
2006/09/08 Javascript
javaScript 简单验证代码(用户名,密码,邮箱)
2009/09/28 Javascript
javascript实现iframe框架延时加载的方法
2014/10/30 Javascript
AngularJS Phonecat实例讲解
2016/11/21 Javascript
深入理解jQuery()方法的构建原理
2016/12/05 Javascript
jQuery插件zTree实现的多选树效果示例
2017/03/08 Javascript
JS判断时间段的实现代码
2017/06/14 Javascript
Vue press 支持图片放大功能的实例代码
2018/11/09 Javascript
layer弹出层扩展主题的方法
2019/09/11 Javascript
用vue设计一个日历表
2020/12/03 Vue.js
初学Python实用技巧两则
2014/08/29 Python
python实现复制整个目录的方法
2015/05/12 Python
python中循环语句while用法实例
2015/05/16 Python
如何在Python函数执行前后增加额外的行为
2016/10/20 Python
浅谈Python类的__getitem__和__setitem__特殊方法
2016/12/25 Python
Python递归函数定义与用法示例
2017/06/02 Python
python使用suds调用webservice接口的方法
2019/01/03 Python
python 使用递归回溯完美解决八皇后的问题
2020/02/26 Python
CSS3 Flex 弹性布局实例代码详解
2018/11/01 HTML / CSS
善意的谎言事例
2014/02/15 职场文书
《鸟的天堂》教学反思
2014/02/27 职场文书
公司周年庆典标语
2014/10/07 职场文书
一年级班主任工作总结2014
2014/11/08 职场文书
幼儿教师个人总结
2015/02/05 职场文书
销售内勤岗位职责范本
2015/04/13 职场文书
关于运动会的广播稿
2015/08/19 职场文书
my.ini优化mysql数据库性能的十个参数(推荐)
2021/05/26 MySQL
Spring mvc是如何实现与数据库的前后端的连接操作的?
2021/06/30 Java/Android
Vue Mint UI mt-swipe的使用方式
2022/06/05 Vue.js