前端知识点回顾之重点篇——JavaScript异步机制
JavaScript异步机制
来源:https://www.cnblogs.com/zhaodongyu/p/3922961.html
JavaScript是单线程异步执行的,单线程意味着代码在任务队列中会按照顺序一个接一个的执行。异步代表JavaScript代码在任务队列中的顺序并不完全等同于代码的书写顺序,比如事件绑定、Ajax、setTimeout()等任务的发生时间是“不可被预期”的。
页面加载时,JavaScript引擎会顺序执行页面上所有JavaScript代码,优先执行同步代码。而异步代码由事件触发引擎按照“事件发生”的顺序添加到JavaScript引擎的任务队列中,待所有同步代码执行结束后,JavaScript引擎会按照任务队列中的顺序来执行异步代码。
知乎上的一段回答:
JavaScript引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序。
浏览器的内核是多线程的,它们在内核控制下相互配合以保持同步,一个浏览器至少实现三个常驻线程:JavaScript引擎线程,GUI渲染线程,浏览器事件触发线程。
- JavaScript引擎是基于事件驱动单线程执行的,JavaScript引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JavaScript线程在运行JavaScript程序。
- GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。但需要注意,GUI渲染线程与JavaScript引擎是互斥的,当JavaScript引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JavaScript引擎空闲时立即被执行。
- 事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JavaScript引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeout、也可来自浏览器内核的其他线程如鼠标点击、Ajax异步请求等,但由于JavaScript的单线程关系所有这些事件都得排队等待JavaScript引擎处理(当线程中没有执行任何同步代码的前提下才会执行异步代码)。
setTimeout()
JavaScript引擎在执行setTimeout(fn, 10)时,一方面继续执行setTimeout(fn, 10)后面的同步代码,同时另一方面开始计时,在10ms之后将fn插入任务队列中。待所有同步代码执行结束后(JavaScript引擎空闲),依次任务队列中的异步代码。所以,setTimeout(fn, 10)并不能准确的在10ms之后执行,而是大于等于10ms。
console.log(1)
setTimeout(function () {console.log('a')}, 10);
setTimeout(function () {console.log('b')}, 0);
var sum = 0;
for (var i = 0; i < 1000000; i ++) {
sum += i;
}
console.log(sum);
setTimeout(function () {console.log('c');}, 0);
输出结果将会是
1
499999500000
b
a
c
代码执行的逻辑如图所示:
如果将for循环上限去掉一个0:
console.log(1)
setTimeout(function () {console.log('a')}, 10);
setTimeout(function () {console.log('b')}, 0);
var sum = 0;
for (var i = 0; i < 100000; i ++) {
sum += i;
}
console.log(sum);
setTimeout(function () {console.log('c');}, 0);
输出结果:
1
4999950000
b
c
a
两段代码的区别在于for循环执行的时间不同,第一段代码的for循环执行时间大于10ms,所以console.log('a')先被插入任务队列,等for循环执行结束后,console.log('c')才被插入任务队列。第二段代码的for循环执行时间小于10ms,所以console.log('c')先被插入任务队列。
setInterval()
setInterval()的执行方式与setTimeout()有不同。假如执行setInterval(fn, 10),则每隔10ms,定时器的事件就会被触发。与setTimeout()相同的是,如果当前没有同步代码在执行(JavaScript引擎空闲),则定时器对应的方法fn会被立即执行,否则,fn就会被加入到任务队列中。由于定时器的事件是每隔10ms就触发一次,有可能某一次事件触发的时候,上一次事件的处理方法fn还没有机会得到执行,仍然在等待队列中,这个时候,这个新的定时器事件就被丢弃,继续开始下一次计时。需要注意的是,由于JavaScript引擎这种单线程异步的执行方式,有可能两次fn的实际执行时间间隔小于设定的时间间隔。比如上一个定时器事件的处理方法触发之后,等待了5ms才获得被执行的机会。而第二个定时器事件的处理方法被触发之后,马上就被执行了。那么这两者之间的时间间隔实际上只有5ms。因此,setInterval()并不适合实现精确的按固定间隔的调度操作。
console.log(1)
var interval = setInterval(function () {
var date = new Date();
console.log(date.getMinutes() + ':' + date.getSeconds() + ':' + date.getMilliseconds());
}, 10);
var sum = 0;
for (var i = 0; i < 1000000; i ++) {
sum += i;
}
console.log(2);
// 清除定时器,避免卡死浏览器
setTimeout(function () {
clearInterval(interval);
}, 100);
输出结果:
1
2
12:32:236
12:32:240
12:32:251
12:32:262
可以看出,setInterval()前两次的间隔时间只有4ms。因为setInterval()第一次被触发后,里面的方法并没有马上被执行,而是等待同步代码执行结束后才被执行,这个过程用了6ms。所以当第一次方法执行过后4ms,第二次方法也被执行了。从setInterval()第二次被触发开始,后面几次的执行都没有被阻塞,所以间隔时间都在11ms左右。
总的来说,setTimeout()和setInterval()都不能满足精确的时间间隔。假如设定的时间间隔为10ms,则setTimeout(fn, 10)中的fn执行的时间间隔可能大于10ms,而setInterval(fn, 10)中fn执行的时间间隔可能小于10ms。
前端知识点回顾之重点篇——JavaScript异步机制的更多相关文章
- 前端知识点回顾之重点篇——AJAX
Ajax(Asynchronous JavaScript and XML) 这种技术就是无须刷新页面即可从服务器中取得数据,但不一定是XML数据.在原生方法上,Ajax技术的核心是XMLHttpReq ...
- 前端知识点回顾之重点篇——ES6的Promise对象
Promise Promise 是异步编程的一种解决方案,比传统的解决方案--回调函数和事件--更合理和更强大. 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异 ...
- 前端知识点回顾之重点篇——ES6的Iterator和Generator
Iterator 迭代器是一种接口.是一种机制. 为各种不同的数据结构提供统一的访问机制.任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员). Iter ...
- 前端知识点回顾之重点篇——CSS中vertical align属性
来源:https://www.cnblogs.com/shuiyi/p/5597187.html 行框的概念 红色(line-height)为行框的顶部和底部,绿色(font-size)为字体的高度, ...
- 前端知识点回顾之重点篇——CSS中flex布局
flex布局 来源: http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html?utm_source=tuicool 采用 Flex 布局的元素 ...
- 前端知识点回顾之重点篇——jQuery实现的原理
jQuery jQuery的实现原理 参考:https://blog.csdn.net/zhouziyu2011/article/details/70256659 外层沙箱和命名空间$ 为了避免声明了 ...
- 前端知识点回顾之重点篇——CORS
CORS(cross origin resource sharing)跨域资源共享 来源:http://www.ruanyifeng.com/blog/2016/04/cors.html 它允许浏览器 ...
- 前端知识点回顾之重点篇——ES6的async函数和module
async函数 ES2017 标准引入了 async 函数,使得异步操作变得更加方便. async 函数是 Generator 函数的语法糖 什么是语法糖? 意指那些没有给计算机语言添加新功能,而只是 ...
- 前端知识点回顾之重点篇——CSS中的BFC
BFC布局(Block Formatting Contexts) 来源:https://www.cnblogs.com/lzbk/p/6057097.html 块级格式化上下文是页面中的一块渲染区域, ...
随机推荐
- 利用axis调用webservice接口
一.首先把wsdl文件放入eclipse中某个项目中的src目录下 二.右键弹出webservice,然后点击webservice菜单,选中genernator client ,选择axis生成Jav ...
- 用Leangoo做敏捷需求管理
转自:https://www.leangoo.com/9229.html 传统的瀑布工作模式使用详细的需求说明书来表达需求,需求人员负责做需求调研,根据调研情况编制详细的需求说明书,进行需求评审,评审 ...
- List.ForEach批量新增并发异常解决
批量新增操作在业务系统中十分常见,尤其是主从表中对从表的批量处理.昨天在对wms主从表进行业务操作时使用了c#中list自带的函数ForEach对从表批量新增,代码如下: 在无并发的情况下接口请求正常 ...
- 配置linux ftp服务器,客户端报list remote folder fail
XFTP出现列表文件夹失败.主要是因为FTP模式不对,应该为主动连接模式, 可以在设置主机属性 - 选项页签 - 将默认勾选的“使用被动模式”(使用消极模式)的多选框取消...就可以了...
- CSP2019 退役记
本来想写"退役在即"的,考完 Day2 后直接改成"退役记"了 Day 0 在 ssf 的机房里继续变弱,自己写了一遍 splay 板子,居然写对了,开心 非常 ...
- Python3+Appium学习笔记04-自动下载apk
在项目中,app更新后,都会发布新的apk版本,然后去蒲公英对应的网站上扫码下载对应的apk进行测试.所以就在想,通过app参数进行安装时,是否可以先通过代码去拉取一遍最新的apk安装包. 因为学 ...
- springboot请求体中的流只能读取一次的问题
场景交代 在springboot中添加拦截器进行权限拦截时,需要获取请求参数进行验证.当参数在url后面时(queryString)获取参数进行验证之后程序正常运行.但是,当请求参数在请求体中的时候, ...
- C语言例题
1.连接两个字符串 将两个字符串连接,不要用stract函数 2.求矩阵外围元素之和 求3行3列矩阵的外围元素之和. 3.求矩阵主对角线和副对角线元素之和 求5行5列矩阵的主对角线和副对角线元素之和. ...
- [BZOJ 1146] [CTSC2008]网络管理Network(树状数组+主席树)
题目描述 M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高 ...
- 如何在 Google 地图中添加标记和说明
JS如下: (function() { window.onload = function() { // Creating an object literal containin ...