Javascript异步编程之setTimeout与setInterval详解分析

一. setTimeout与setInterval详细分析基本原理。

接下来这篇博客会总结setTimeout和setInterval基本点,对于上面三点会分三篇博客分别来总结,对于知道上面三点的人,但是又不是非常了解全面知识点的码农来说,没有关系的,我们可以慢慢来学习,来理解,或者我总结不全面的或者不好地方可以留言,学习本来就是要互动,才有提高。当然对于那些知识大牛来说,也可以看下,如果我总结不好的话,也可以提提意见,我也可以多学习学习下!

在研究setTimeout与setInterval之前,我们可以先来看看一个小小的demo,其实总结与研究就是要多做demo,因为有的事情我们看起来很简单,真正做起来的时候不是那么一回事。比如如下:

for(var i = 1; i <= 3; i++) {

setTimeout(function(){

console.log(i);

},100);

}```

如果javascript语言不是很熟悉的话,很多人会理所当然的认为for循环会分别打印出1,2,3. 但是事实不是这样的,会输出3次4. 要理解为什么会打印三次4,我们先来理解setTimeout这个函数吧,很多人会认为上面的setTimeout的意思是这样的,在100毫秒后执行setTimeout的回调函数,其实这样的理解是有误的,其实setTimeout与setInterval真正的含义如下:

setTimeout:在指定的毫秒数后,将定时任务处理的函数添加到执行队列的队尾。
setInterval:按照指定的周期(以毫秒数计时),将定时任务处理函数添加到执行队列的队尾。
setTimeout与setInterval且都是异步的,所以我们现在可以来理解下上面循环为什么一直都是4呢?其实调用setTimeout时候,会有一个延时事件排入队列,然后setTimeout调用之后的那行代码运行,接着是再下一行代码,直到再也没有任何代码了,javascript虚拟机才会问,队列里还有吗?如果队列中至少有一个事件适合于触发,比如上面的setTimeout函数,则会调用setTimeout那个函数。所以上面的代码先for循环,循环结束,而 i === 4一直递增,直到不再满足i<=3为止。所以就打印了3个4.

我们再来看看下面的函数,如下:

setTimeout(function(){

console.log("打印我,我是异步执行的");

},100);```

console.log("我是新来的,我要先执行");

运行结果是:先打印出 “我是新来的,我先执行”这句代码,接着打印”打印我,我是异步执行的”代码。

二:理解javascript线程。

Javascript引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行的。

那么单线程是如何配合浏览器内核处理这些定时器和相应浏览器事件呢?

浏览器内核允许多个线程异步执行,这些线程在内核控制下相互配合以保持同步,比如一个浏览器至少有3个以上的线程,有:javascript引擎线程,界面渲染线程,浏览器事件触发线程,除这些以外,也有一些执行完的线程,比如http请求线程,这些异步线程都会产生不同的异步的事件。

界面渲染线程:

该线程负责渲染浏览器HTML界面元素,当界面需要重绘或由于某种操作引发回流(reflow),该线程就会执行,该线程与javascript引擎线程是互斥的,因为javascript引擎运行脚本期间,浏览器渲染线程都是出于挂起状态的,比如我们常见的是在页面head标签内不建议把JS放在头部的原因,希望要把JS放在尾部或者使用异步加载等操作。因此在脚本中执行对界面进行更新操作,如动态添加节点或者删除节点等更新会把这些事件放在队列当中,等javascript引擎空闲时才有机会渲染出来。

浏览器事件触发线程:

用户单击一个已附加有单击事件处理器dom元素时,会有一个单击事件排入队列,但是该单击事件处理器要等到当前所有正在运行的代码均已结束才会执行。

比如如下一个小demo,我们平时写代码时候,特别用原审javascript写tab切换的时候,经常会碰到如下代码,比如点击一个li标签,希望切换到对应的内容上来。如下点击事件demo。我这里使用jquery来演示下:

HTML代码如下结构:

< li class="container">点击我1</ li >
< li class="container">点击我2</ li >

< li class="container">点击我3</ li >```

JS如下:

var lists = $(".container");

for(var i = 0, ilen = lists.length; i < ilen; i++) {

$(lists[i]).bind('click',function(){

console.log(i); // 打印3

});

}```

上面的代码点击一下,打印出3(不是0,1,2),原理还是和上面一样。

定时触发线程:

这里谈到的定时计数器不是由javascript引擎计数的,因为javascript引擎是单线程的,如果处于堵塞状态就计不了时的,它必须依赖外部计时并触发定时,所以队列中的定时事件也是异步事件。

三:理解setTimeout与setInterval异步事件:

Javascript最基础的异步函数是setTimeout与setInterval,setTimeout会在一定的时间后执行相应的函数,它接受一个回调函数和一个毫秒时间,比如如下:

console.log( "a" );

setTimeout(function() {

console.log( "c" )

}, 500 );

setTimeout(function() {

console.log( "d" )

}, 500 );

setTimeout(function() {

console.log( "e" )

}, 500 );

console.log( "b" );```

控制台先输出“a”、“b”,大约500毫秒后,再看到“c”、“d”、“e”。

但是如果我把第一个setTimeout的延时时间改大一点或者改为600毫秒,那么打印出来就分别是a,b,d,e,c了。你可能听过事件循环这个词,它是用于描述队列的工作方式的。当异步函数执行时,回调函数就会被压入这个队列里面,,但是如果延迟时间不一样的话,那么就不会了,就像上面的列子把定时毫秒数改大点输出来的就不一样了。

#四:异步函数的类型

在Javascript环境中提供的异步函数分为2大类:I/O函数和计时函数。

我们都知道创建nodeJS不是为了在服务器上运行javascript,而是因为javascript语言可以完美的实现非堵塞式的I/O。比如典型的ajax请求,如下代码:

var url = "http://localhost/setTimeout/index2.php";

var xhr=new XMLHttpRequest;

xhr.open("GET","http://localhost/setTimeout/index2.php",true);

xhr.send();

xhr.onreadystatechange=function(){

if(xhr.readyState<4)return;

;

};

;


运行结果后先执行”Ajax还没完成呢?”,后执行onreadystatechange的回调函数。在ajax函数中先执行send方法后,再绑定事件呢,而不是先绑定事件,再send呢?

其实xhr对象使用了其他线程,这里涉及到一些跨线程通信的问题,跨线程访问数据时需要使用委托,否则会发生数据冲突,所谓委托其实就是一个线程向另一个线程发送消息,但是xhr线程想要触发主线程xhr对象的onreadystatechange事件就需要委托,而主线程目前是忙碌状态,它正在出理初始化消息,只有等到初始化消息空闲后才会执行子线程的委托处理,而初始化消息空闲时就意味着onreadystatechange事件被绑定上了,所以后面的代码执行会永远比xhr线程执行要快。所以先会执行后面的alert对话框,再执行onreadystatechange事件。当然ajax请求第三个参数我们可以设置成false,同步请求,一般情况下还是异步请求好,但是为了处理一些特殊的需求,也可以设置同步请求(注意:同步请求会堵塞浏览器加载,所以如果请求的数据很大的时候,还是考虑异步请求。),比如一些常见的需求,发送ajax请求后,要打开一个新窗口这样的一个需求,我们都知道如果是异步请求chrome和firefox直接会被拦截掉,但是如果我设置了同步请求就可以实现发送ajax请求后,再打开一个新窗口了。

我们已经看到,异步函数非常适用于I/O操作,但是我们现在想让一个函数在将来某时刻来运行或者一个动画函数在将来某个时候来执行动画效果,这时候我们会想到javascript中的setTimeout与setInterval函数了。但是setTimeout与setInterval有如下缺陷:

当同一个javascript进程运行的代码时候,任何javascript计时函数都无法使代码运行起来,如下demo测试:

var start = new Date;

stTimeout(function(){

var end = new Date;

console.log("Time:",end-start,'ms');

},500);

while(new Date - start < 1000) {

}```

想打印出上面的console.log, 在浏览器一直刷新看到,第一次1020ms,第二次1029ms,反正结果一直是1s以上,也就是说后面的函数如果执行时间非常长的话,那么setTimeout代码永远不会执行。

  1. setInterval根据HTML规范可知:在一个小时之内会延迟 4-5ms这么一个延迟。也就是说使用这个计时不是非常精确。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容