你知道setTimeout是如何运行的吗-
你真的了解setTimeout是如何运作的吗?这篇文章将带你深入了解setTimeout的工作原理,如果你对此感兴趣,不妨继续往下看。
让我们看一段代码,猜猜它的执行结果:
```javascript
var start = new Date;
setTimeout(function(){
console.log('时间流逝了:'+(new Date - start)+'毫秒');
}, 200);
while (new Date - start < 1000) {}
console.log(1);
function doSoming(){
setTimeout(function(){
console.log('时间又流逝了:'+(new Date - start)+'毫秒');
},10);
}
doSoming();
while (new Date - start < 2000) {}
console.log(2);
```
执行结果如下:
约1秒后输出1,再过约1秒后输出2,接着才立即输出时间流逝了: 2002毫秒,最后输出时间又流逝了: 2003毫秒。你答对了吗?
接下来,让我们一起一下其中的原理。
在现有的浏览器环境中,JavaScript执行引擎是单线程的。这意味着主线程的语句和方法会阻塞定时任务的运行。在JavaScript执行引擎之外,存在一个任务队列。当我们在代码中调用setTimeout()方法时,注册的延时方法会被添加到浏览器内核的其他模块进行处理。
当延时方法到达触发条件,即达到设置的延时时间时,该模块会将其添加到任务队列中。这一过程与执行引擎主线程独立,执行引擎在主线程方法执行完毕并达到空闲状态时,才会从该模块的任务队列中提取任务来执行。这期间的时间可能大于注册任务时设置的延时时间。
浏览器在空闲状态下,会不断地从模块的任务队列中提取任务,这被称为事件循环模型。
再回头看一下之前的代码,第二个setTimeout()的延迟方法的延迟时间只有10毫秒,比第一个要早触发。但为什么执行结果却在后面呢?这是因为它被之前的代码阻塞了约1000毫秒(视浏览器的处理速度而定)。当它挂到处理模块并等到触发时间添加进任务队列时,第一个setTimeout()的延迟方法已经添加到模块的任务队列中了。由于引擎主线程是按顺序提取任务来执行的,因此出现了上述的执行结果。
现在,如果你把上面的while循环的条件改为while (new Date - start < 189) {}或者是while (new Date - start < 190) {},结果又会是什么呢?你可以试着刷新浏览器多次,观察结果。
setInterval()方法和setTimeout()的原理是相同的。调用setInterval()方法时,注册的延时方法也会挂到模块处理。每当触发时间到达,就会往任务队列中添加一次要执行的方法。
setTimeout的语法如下:
```javascript
var timeID = window.setTimeout(func, delay, [param1, param2, ...]);
var timeID = window.setTimeout(code, delay);
```
兼容IE的回调函数参数传递方式
为了确保我们的代码能够在古老的IE浏览器中顺畅运行,我们需要对setTimeout函数进行一些调整。当document.all属性存在且window.setTimeout.isPolyfill不存在时,我们进行以下操作:
循环中的闭包陷阱
在JavaScript中,闭包是一个强大的工具,但如果不正确使用,也可能导致一些难以预料的错误。例如,下面的代码:
```javascript
for (var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
```
看似简单,但结果却不尽人意。你可能会期待输出0到9这些数字,但实际上,你会看到连续的10次输出“10”。这是因为闭包保留了变量i的引用,当setTimeout中的函数被调用时,循环已经结束,i的值已经变成了10。为了解决这个问题,我们需要让每次循环都创建一个i的副本。我们可以使用立即执行的匿名函数来达到这个目的:
```javascript
for (var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
```
外部的匿名函数会立即执行,并将当前的i值作为参数e传递给它。这样,在setTimeout的回调函数中使用的e变量就是i的一个副本,其值在循环过程中不会改变。另一种实现方式是返回一个函数:
```javascript
for (var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000);
}
```
还有一个重要的应用——函数节流与函数去抖。这些技术在实际开发中非常有用。关于它们的详细资料,您可以参考我收集的一些网上资源。希望这篇文章能对您有所启发,也欢迎您关注我们的博客了解更多内容。狼蚁SEO一直为大家提供有价值的文章,感谢大家的支持!记得查看我们的参考链接了解更多信息。更多精彩内容等待您的!cambrian.render('body')也欢迎您去体验更多的精彩功能。
微信营销
- 你知道setTimeout是如何运行的吗-
- 我想和你结婚做炙热的亲吻
- JavaScript实现可拖拽的拖动层Div实例
- vue-awesome-swiper 基于vue实现h5滑动翻页效果【推荐
- js实现照片墙功能实例
- javascript实现多级联动下拉菜单的方法
- 昌平购房攻略:如何挑选心仪的房产
- 理解JS事件循环
- jQuery实现购物车表单自动结算效果实例
- SpringBoot集成jsp(附源码)+遇到的坑
- vue.js 1.x与2.0中js实时监听input值的变化
- jstree创建无限分级树的方法【基于ajax动态创建子
- 使用vue的transition完成滑动过渡的示例代码
- 夏目友人帐主题曲
- 用ASP VBS xmlhttp adodbstream下载和保存图片的代码
- js实现图片漂浮效果的方法