栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > Web开发 > JavaScript

从setTimeout理解JS运行机制

JavaScript 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

从setTimeout理解JS运行机制

引言

setTimeout函数用来指定某个函数或某段代码,在多少毫秒之后执行。它返回一个整数,表示定时器(timer)的编号,以后可以用来取消这个定时器。

1. 例子
console.log(1);
setTimeout(function () {    console.log(2);
}, 0);console.log(3);

问:最后的打印顺序是什么?
这个问题如果不了解js的运行机制就会答错
正确答案是:1 3 2

无论setTimeout的执行时间是0还是1000,结果都是先输出3后输出2,这就是面试官常常考查的js运行机制的问题,接下来我们要引入一个概念,Javascript 是单线程的。

2. Javascript 单线程

Javasscript引擎是基于事件驱动单线程执行的,JS引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行JS程序。

说的通俗一点就是JS在同一时间内只能做一件事,这也常被称为 “阻塞式执行”。

2.1 任务队列

那么单线程的Javasscript是怎么实现诸如事件点击触发回调函数、ajax通信、计时器这种异步处理的东西呢?
答案是任务队列
所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。

2.1.1 同步任务

同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。

  • 输出

  • 变量的声明

2.1.2 异步任务
  • setTimeout和setInterval

  • DOM事件

  • ES6中的Promise

3. setTimeout运行机制

setTimeout和setInterval的运行机制是,将指定的代码移出本次执行,等到下一轮Event Loop时,再检查是否到了指定时间。如果到了,就执行对应的代码;如果不到,就等到再下一轮Event Loop时重新判断。这意味着,setTimeout指定的代码,必须等到本次执行的所有代码都执行完,才会执行。

优先关系:异步任务要挂起,执行同步任务,同步任务执行完毕才会进行异步任务。

4. 例子
console.log('A');
setTimeout(function () {    console.log('B');
}, 0);while (1) {}

大家再猜一下这个输出的结果会是什么?
正确答案是:A

注:必须先注释掉代码,执行后一定要会强制删除进程,不然会卡住。

同步队列输出A之后,陷入while(true){}的死循环中,异步任务不会被执行。类似的,有时addEventListener()方法监听点击事件click,用户点了某个按钮会卡死,就是因为当前JS正在处理同步队列,无法将click触发事件放入执行栈,不会执行,出现“假死”。

5. 例子
for (var i = 0; i < 4; i++) {
    setTimeout(function () {        console.log(i);
    }, 1000);
}

正确答案是:4 4 4 4

for循环是一个同步任务,为什么连续输出四个4?
因为有队列插入的时间
执行时间从1000改成0,还是四个4。
那么这个问题是如何产生和解决的呢?请接着阅读

5.1 异步队列执行的时间

执行到异步任务的时候,会直接放到异步队列中吗?答案是不一定的。
因为浏览器有个timer模块,定时器到了执行时间才会把异步任务放到异步队列。for循环体执行的过程中并没有把setTimeout放到异步队列中,只是交给定时器模块了。4个循环体执行速度非常快。定时器到了设置的时间才会把setTimeout语句放到异步队列中,即使setTimeout设置的0毫秒,也按4毫秒算(原因见下方段落引用)。
这就解释了例5为什么会连续输出四个4的原因。

HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加。在此之前,老版本的浏览器都将最短间隔设为10毫秒。

5.1.1 利用闭包实现setTimeout等时循环 - 间歇调用
for (var i = 0; i < 4; i++) {
    setTimeout(function () {        console.log(i);
    }, 1000);
}

这里也可以用setInterval方法来实现间歇调用。
详见:setTimeout和setInterval的区别

以上、第5小结的难点是异步队列的放入时间和执行时间。

6. 事件循环 Event Loop

主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop。

有时候明明写的延时3秒,实际却5,6秒才执行函数,这又是因为什么?

setTimeout 并不能保证执行的时间,是否及时执行取决于 Javascript 线程是拥挤还是空闲。浏览器的JS引擎遇到setTimeout,拿走之后不会立即放入异步队列,同步任务执行之后,timer模块会到设置时间之后放到异步队列中。js引擎发现同步队列中没有要执行的东西了,即运行栈空了就从异步队列中读取,然后放到运行栈中执行。这时setTimeout函数体就变成了运行栈中的同步任务,空了再监听异步队列中有没有,如果有就继续执行,如此循环,就叫Event Loop。

总结

Javascript通过事件循环和浏览器各线程协调共同实现异步



作者:番茄沙司a
链接:https://www.jianshu.com/p/3facc9bd87f


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/247173.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号