你知道setTimeout是如何运行的吗-

网络营销 2025-04-05 11:53www.168986.cn短视频营销

你真的了解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')也欢迎您去体验更多的精彩功能。

上一篇:我想和你结婚做炙热的亲吻 下一篇:没有了

Copyright © 2016-2025 www.168986.cn 狼蚁网络 版权所有 Power by