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生命周期详细过程

  2. Outline 科学的上网

    outline 官网:https://getoutline.org/zh-CN/home 下载 Outline 管理器 下载 Outline 客户端 配置浏览器代理

  3. 11.SpringMVC注解式开发-处理器方法的返回值

    处理器方法的返回值 使用@Controller 注解的处理器的处理器方法,其返回值常用的有四种类型 1.ModelAndView 2.String 3.void 4.自定义类型对象 1.返回Model ...

  4. idea控制台乱码修改

    我的idea当前版本是2019.2.2 试了很多,只有这个有效果 工具类→HELP→Edit Custom VM OPtions中加 -Dfile.encoding=utf-8 然后重启IDEA 这个 ...

  5. 四:MVC之LINQ方法语法

    linq 查询 有两种语法  ,前面我们说了一种,接下来说方法语法(我读着一直很绕口) 查询语法,方法语法 ------------------------以下文字都是复制-------------- ...

  6. 13_Redis_持久化

    一:概述: Redis的高性能是山于其将所有数据都存储在了内存中,为了使Redis在重启之后仍能保证数据不丢失,需要将数据从内存中同步到硬盘中,这一过程就是持久化. Redis支持两种方式的持久化,一 ...

  7. 2. An Array of Sequences

    1. Overview of Built-In Sequences Container sequences: list, tuple, and collections.deque can hold i ...

  8. scala学习(4)---Array定长数组操作

    ScalaArrayNote: https://www.jianshu.com/p/d906f00c05bf

  9. java线程基础巩固---如何实现一个自己的显式锁Lock

    拋出synchronized问题: 对于一个方法上了同锁如果被一个线程占有了,而假如该线程长时间工作,那其它线程不就只能傻傻的等着,而且是无限的等这线程工作完成了才能执行自己的任务,这里来演示一下这种 ...

  10. Vue项目中的文件导出

    项目中涉及到文件导出,分xml和excel导出.不同的文件导出格式不同,需要根据文件类型判断导出格式. exportAllData(val){ //全部导出 if(!val){ this.export ...