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


Posted in Javascript onAugust 16, 2016

大家看下如下代码,猜猜执行结果:

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 毫秒

您猜对了没?
这里通过setTimeout来延迟执行的函数都被推到最后才执行了;
原理如下:

在现有浏览器环境中,Javascript执行引擎是单线程的,主线程的语句和方法,会阻塞定时任务的运行,在Javascript执行引擎之外,存在一个任务队列,当在代码中调用setTimeout()方法时,注册的延时方法会挂到浏览器内核其他模块处理,当延时方法到达触发条件,即到达设置的延时时间时,该模块再将要执行的方法添加至该模块的任务队列中。这一过程与执行引擎主线程独立,执行引擎在主线程方法执行完毕,到达空闲状态时,才会从该模块的任务队列中顺序提取任务来执行,这期间的时间,可能大于注册任务时设置的延时时间;

浏览器在空闲状态下,会不断的尝试从模块的任务队列中提取任务,这称为事件循环模型;

再回头看下前面的代码,第二个setTimeout()的延迟方法的延迟时间是10毫秒,比第一个要早触发啊!为什么执行结果却在后面?因为它被之前的代码阻塞了约1000.5~1001毫秒了(视浏览器的处理速度),等他挂到处理模块,等到触发时间添加进任务队列时,第一个setTimeout()的延迟方法早就被添加到模块的任务队了,而引擎主线程是按顺序提取得,所以,你应该懂了吧?

现在,如果把上面的while (new Date - start < 1000) {}改成while (new Date - start < 189) {}或者是while (new Date - start < 190) {},结果又是什么?我就不多说了!各刷新浏览器20遍,自己看结果吧!

而setInterval()方法和setTimeout()地位是相同的,调用setInterval()方法时,注册的延时方法挂到模块处理,每当触发时间到达,就往任务队列添加一次要执行的方法;

下面来具体看看setTimeout的语法: 
var timeID = window.setTimeout(func,delay,[param1,param2,...]); 
var timeID = window.setTimeout(code,delay);
setTimeout和setInterval是Window对象的方法(可省略window),第二个之后的可选参数(IE9及旧版不支持)是传递给func的参数,每次调用他们时都会返回一个数字ID(在浏览器中打印出来就只是个数字,而本人在webstorm中打印出来发现它实际是一个对象,有很多个属性),这个ID保持着它对应的setTimeout或setInterval的相关信息,主要用来在中模块中和任务队列中清除(或关闭)掉它们(用方法clearTimeout(ID)和clearInterval(ID))。
如果你需要向你的回调函数内传递一个参数以下是兼容IE的写法 

if (document.all && !window.setTimeout.isPolyfill) {
 var __nativeST__ = window.setTimeout;
 window.setTimeout = function (vCallback, nDelay, param1, param2,param3) {
 var aArgs = Array.prototype.slice.call(arguments, 2);
 return __nativeST__(vCallback instanceof Function ? function () {
  vCallback.apply(null, aArgs);
 } : vCallback, nDelay);
 };
 window.setTimeout.isPolyfill = true;
}

一个常见的错误出现在循环中使用闭包 

for(var i =0; i <10; i++){
 setTimeout(function(){
  console.log(i); 
 },1000);
}

上面的代码只会输出数字 10 十次。为什么?闭包! 

当 console.log 被调用的时候,虽然匿名函数保持对外部变量 i 的引用,但此时 for循环已经结束, i 的值被修改成了 10. 

为了得到想要的结果,需要在每次循环中创建变量 i 的拷贝。 

为了正确的获得循环序号,最好使用 匿名包裹器(其实就是我们通常说的自执行匿名函数)。 

for(var i =0; i <10; i++){
 (function(e){
  setTimeout(function(){
   console.log(e); 
  },1000);
 })(i);
}

外部的匿名函数会立即执行,并把 i 作为它的参数,此时函数内 e 变量就拥有了 i 的一个拷贝。
 当传递给 setTimeout 的匿名函数执行时,它就拥有了对 e 的引用,而这个值是不会被循环改变的。
 有另一个方法完成同样的工作;那就是从匿名包装器中返回一个函数。这和上面的代码效果一样。 

for(var i =0; i <10; i++){
 setTimeout((function(e){
  return function(){
   console.log(e);
  }
 })(i),1000)
}

还有一个重要应用:函数节流(throttle)与函数去抖(debounce)
请看我从网上收集的一些资料:
JavaScript-性能优化之函数节流(throttle)与函数去抖(debounce)

参考链接: 
https://developer.mozilla.org/zh-CN/docs/Web/API/Window/setTimeout 
http://www.alloyteam.com/2015/10/turning-to-javascript-series-from-settimeout-said-the-event-loop-model/#prettyPhoto

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
类似框架的js代码
Nov 09 Javascript
js数字输入框(包括最大值最小值限制和四舍五入)
Nov 24 Javascript
JavaScript中的函数声明和函数表达式区别浅析
Mar 27 Javascript
JS实现点击复选框将按钮或文本框变为灰色不可用的方法
Aug 11 Javascript
Extjs4.0 ComboBox如何实现三级联动
May 11 Javascript
Angular中ng-repeat与ul li的多层嵌套重复问题
Jul 24 Javascript
原生javascript实现文件异步上传的实例讲解
Oct 26 Javascript
jquery.onoff实现简单的开关按钮功能(推荐)
May 24 jQuery
简化版的vue-router实现思路详解
Oct 19 Javascript
axios如何取消重复无用的请求详解
Dec 15 Javascript
vue中的过滤器及其时间格式化问题
Apr 09 Javascript
学习 Vue.js 遇到的那些坑
Feb 02 Vue.js
深入理解(function(){... })();
Aug 16 #Javascript
关于JSON与JSONP简单总结
Aug 16 #Javascript
json与jsonp知识小结(推荐)
Aug 16 #Javascript
浅谈JS继承_借用构造函数 &amp; 组合式继承
Aug 16 #Javascript
JS读写CSS样式的方法汇总
Aug 16 #Javascript
浅谈JS继承_寄生式继承 &amp; 寄生组合式继承
Aug 16 #Javascript
jQuery EasyUI Tab 选项卡问题小结
Aug 16 #Javascript
You might like
php中计算未知长度的字符串哪个字符出现的次数最多的代码
2012/08/14 PHP
php 二维数组时间排序实现代码
2016/11/19 PHP
PHP实现的权重算法示例【可用于游戏根据权限来随机物品】
2019/02/15 PHP
php数值计算num类简单操作示例
2020/05/15 PHP
表单切换,用回车键替换Tab健(不支持IE)
2011/07/20 Javascript
jquery 实现表单验证功能代码(简洁)
2012/07/03 Javascript
jQuery中iframe的操作(点击按钮新增窗口)
2016/04/20 Javascript
全面理解JavaScript中的闭包
2016/05/12 Javascript
vue开发心得和技巧分享
2016/10/27 Javascript
js实现3D图片展示效果
2017/03/09 Javascript
使用vue-route 的 beforeEach 实现导航守卫(路由跳转前验证登录)功能
2018/03/22 Javascript
使用JavaScript中的lodash编写双色球效果
2018/06/24 Javascript
解决layer.confirm快速点击会重复触发事件的问题
2019/09/23 Javascript
Jquery高级应用Deferred对象原理及使用实例
2020/05/28 jQuery
使用Python实现租车计费系统的两种方法
2018/09/29 Python
学生信息管理系统python版
2018/10/17 Python
Python中利用aiohttp制作异步爬虫及简单应用
2018/11/29 Python
Python操作mongodb数据库的方法详解
2018/12/08 Python
用python建立两个Y轴的XY曲线图方法
2019/07/08 Python
Python Django基础二之URL路由系统
2019/07/18 Python
在python Numpy中求向量和矩阵的范数实例
2019/08/26 Python
将python安装信息加入注册表的示例
2019/11/20 Python
python GUI库图形界面开发之PyQt5滑块条控件QSlider详细使用方法与实例
2020/02/28 Python
Python修改列表值问题解决方案
2020/03/06 Python
Python 多进程原理及实现
2020/12/21 Python
CSS3中的@keyframes关键帧动画的选择器绑定
2016/06/13 HTML / CSS
职业生涯规划书的格式
2013/12/29 职场文书
房地产融资计划书
2014/01/10 职场文书
小学校长汇报材料
2014/08/20 职场文书
暑假安全教育广播稿
2014/09/10 职场文书
2014国庆节幼儿园亲子活动方案
2014/09/16 职场文书
同学毕业留言寄语
2015/02/27 职场文书
2015年秋季运动会前导词
2015/07/20 职场文书
python爬虫selenium模块详解
2021/03/30 Python
tp5使用layui实现多个图片上传(带附件选择)的方法实例
2021/11/17 PHP
《最终幻想14》6.01版本4月5日推出 追加新任务新道具
2022/04/03 其他游戏