JavaScript异步机制

来源:https://www.cnblogs.com/zhaodongyu/p/3922961.html

JavaScript是单线程异步执行的,单线程意味着代码在任务队列中会按照顺序一个接一个的执行。异步代表JavaScript代码在任务队列中的顺序并不完全等同于代码的书写顺序,比如事件绑定、Ajax、setTimeout()等任务的发生时间是“不可被预期”的。

页面加载时,JavaScript引擎会顺序执行页面上所有JavaScript代码,优先执行同步代码。而异步代码由事件触发引擎按照“事件发生”的顺序添加到JavaScript引擎的任务队列中,待所有同步代码执行结束后,JavaScript引擎会按照任务队列中的顺序来执行异步代码。

知乎上的一段回答:

JavaScript引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序。

浏览器的内核是多线程的,它们在内核控制下相互配合以保持同步,一个浏览器至少实现三个常驻线程:JavaScript引擎线程GUI渲染线程浏览器事件触发线程

  1. JavaScript引擎是基于事件驱动单线程执行的,JavaScript引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JavaScript线程在运行JavaScript程序。
  2. GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。但需要注意,GUI渲染线程与JavaScript引擎是互斥的,当JavaScript引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JavaScript引擎空闲时立即被执行。
  3. 事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待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异步机制的更多相关文章

  1. 前端知识点回顾之重点篇——AJAX

    Ajax(Asynchronous JavaScript and XML) 这种技术就是无须刷新页面即可从服务器中取得数据,但不一定是XML数据.在原生方法上,Ajax技术的核心是XMLHttpReq ...

  2. 前端知识点回顾之重点篇——ES6的Promise对象

    Promise Promise 是异步编程的一种解决方案,比传统的解决方案--回调函数和事件--更合理和更强大. 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异 ...

  3. 前端知识点回顾之重点篇——ES6的Iterator和Generator

    Iterator 迭代器是一种接口.是一种机制. 为各种不同的数据结构提供统一的访问机制.任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员). Iter ...

  4. 前端知识点回顾之重点篇——CSS中vertical align属性

    来源:https://www.cnblogs.com/shuiyi/p/5597187.html 行框的概念 红色(line-height)为行框的顶部和底部,绿色(font-size)为字体的高度, ...

  5. 前端知识点回顾之重点篇——CSS中flex布局

    flex布局 来源: http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html?utm_source=tuicool 采用 Flex 布局的元素 ...

  6. 前端知识点回顾之重点篇——jQuery实现的原理

    jQuery jQuery的实现原理 参考:https://blog.csdn.net/zhouziyu2011/article/details/70256659 外层沙箱和命名空间$ 为了避免声明了 ...

  7. 前端知识点回顾之重点篇——CORS

    CORS(cross origin resource sharing)跨域资源共享 来源:http://www.ruanyifeng.com/blog/2016/04/cors.html 它允许浏览器 ...

  8. 前端知识点回顾之重点篇——ES6的async函数和module

    async函数 ES2017 标准引入了 async 函数,使得异步操作变得更加方便. async 函数是 Generator 函数的语法糖 什么是语法糖? 意指那些没有给计算机语言添加新功能,而只是 ...

  9. 前端知识点回顾之重点篇——CSS中的BFC

    BFC布局(Block Formatting Contexts) 来源:https://www.cnblogs.com/lzbk/p/6057097.html 块级格式化上下文是页面中的一块渲染区域, ...

随机推荐

  1. vue项目js实现图片放大镜功能

    效果图:   我写的是vue的组件形式,方便复用,图片的宽高,缩放的比例可以自己定义 magnifier.vue <template> <div class="magnif ...

  2. InnoDB意向锁的作用

    https://www.zhihu.com/question/51513268?sort=created IX,IS是表级锁,不会和行级的X,S锁发生冲突.只会和表级的X,S发生冲突. 意向锁是一种快 ...

  3. JAVA 内存模型(主内存,工作内存)

    JVM将内存组织为主内存和工作内存两个部分. 主内存是所有的线程所共享的,主要包括本地方法区和堆. 每个线程都有一个工作内存不是共享的,工作内存中主要包括两个部分: 1:一个是属于该线程私有的栈; 2 ...

  4. pip安装超时解决方案

    1 安装的后面 用-i接一些国内的镜像,下面这个是清华的,亲测比较快 pip install apache-airflow -i https://pypi.tuna.tsinghua.edu.cn/s ...

  5. sklearn--回归

    一.线性回归 LinearRegression类就是我们平时所说的普通线性回归,它的损失函数如下所示: 对于这个损失函数,一般有梯度下降法和最小二乘法两种极小化损失函数的优化方法,而scikit-le ...

  6. Linux学习笔记(三)Linux常用命令:链接命令和文件查找命令

    一.链接命令 ln -s [原文件] [目标文件] (link) -s意为创建软连接 硬链接和软连接 硬链接的特点: (1)拥有相同的 i 结点和block块,可以看作是同一个文件 (2)可以通过 i ...

  7. 用实例的方式去理解storm的并发度

    什么是storm的并发度 一个topology(拓扑)在storm集群上最总是以executor和task的形式运行在suppervisor管理的worker节点上.而worker进程都是运行在jvm ...

  8. PAT Basic 1063 计算谱半径 (20 分)

    在数学中,矩阵的“谱半径”是指其特征值的模集合的上确界.换言之,对于给定的 n 个复数空间的特征值 { , },它们的模为实部与虚部的平方和的开方,而“谱半径”就是最大模. 现在给定一些复数空间的特征 ...

  9. Codeforces 1149 B - Three Religions

    B - Three Religions 思路:dp dp[i][j][k]:a的前i个和b的前j个和c的前k个能构成的最前面的位置 删字符时状态不用改变,加字符时只会改变1*250*250个状态 代码 ...

  10. Redis五种数据结构(二)

    Redis数据结构 Redis数据结构介绍 Redis是一种高级的key-value的存储系统,其中value支持五种数据类型. 字符串(String) 哈希(hash) 字符串列表(list) 字符 ...