Javascript异步编程之setTimeout与setInterval详解

http://www.cnblogs.com/tugenhua0707/

在谈到异步编程时,本人最主要会从以下三个方面来总结异步编程( 注意: 特别解释:是总结,本人也是菜鸟,所以总结不好的,请各位大牛多多原谅!)

1. 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真正的含义如下:

  1. setTimeout:在指定的毫秒数后,将定时任务处理的函数添加到执行队列的队尾。
  2. 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引擎直到异步函数执行完,才会开始出来这个事件循环,这意味着javascript也并不是多线程的,事件循环是一个先进先出的(FIFO)队列,这说明回调是按照他们被加入队列的顺序执行的(在相同的情况下。),但是如果延迟时间不一样的话,那么就不会了,就像上面的列子把定时毫秒数改大点输出来的就不一样了。

四:异步函数的类型

在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;

alert(xhr.responseText);

};

alert("Ajax还没完成呢?");

运行结果后先执行”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有如下缺陷:

  1. 当同一个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代码永远不会执行。

2. setInterval根据HTML规范可知:在一个小时之内会延迟 4-5ms这么一个延迟。也就是说使用这个计时不是非常精确

时间: 2024-10-07 05:30:11

Javascript异步编程之setTimeout与setInterval详解的相关文章

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

Javascript异步编程之setTimeout与setInterval 在谈到异步编程时,本人最主要会从以下三个方面来总结异步编程( 注意: 特别解释:是总结,本人也是菜鸟,所以总结不好的,请各位大牛多多原谅!) 1. setTimeout与setInterval详细分析基本原理. 接下来这篇博客会总结setTimeout和setInterval基本点,对于上面三点会分三篇博客分别来总结,对于知道上面三点的人,但是又不是非常了解全面知识点的码农来说,没有关系的,我们可以慢慢来学习,来理解,或

setTimeOut和setInterval详解

setTimeout和setInterval的语法相同.它们都有两个参数,一个是将要执行的代码字符串,还有一个是以毫秒为单位的时间间隔,当过了那个时间段之后就将执行那段代码.不过这两个函数还是有区别的: setInterval在执行完一次代码之后,经过了那个固定的时间间隔,它还会自动重复执行代码,而setTimeout只执行一次那段代码.区别:window.setTimeout("function",time)://设置一个超时对象,只执行一次,无周期 window.setInterv

unix网络编程之listen()详解

Unix网络编程描述如下: #include <sys/socket.h> int listen(int sockfd,  int backlog); 返回:若成功则为0,  若出错则为-1: 本函数通常应该在调用socket和bind这两个函数之后,并在调用accept函数之前调用: 为了理解其中的backlog参数,我们必须认识到内核为任何一个给定的监听套接字维护两个队列: (1)未完成连接队列(incomplete connection queue),

JavaScript中SetInterval与setTimeout的用法详解

在写H5游戏时经常需要使用定时刷新页面实现动画效果,比较常用即setTimeout()以及setInterval(),但是大家对SetInterval与setTimeout的用法了解吗,下面通过本文给大家详解js中SetInterval与setTimeout的用法,需要的朋友参考下 setTimeout 描述 setTimeout(code,millisec) setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式. 注:调用过程中,可以使用clearTimeout(id_of_

net异步编程之await

net异步编程之await 初探asp.net异步编程之await 终于毕业了,也顺利进入一家期望的旅游互联网公司.27号入职.放肆了一个多月没写代码,好方啊. 另外一下观点均主要针对于await. 写在前面(带着问题学习) 一.根据代码和执行结果,初探异步编程的执行过程. *问题1:await会让当前线程一直等待吗?await等待的时间中一直占用线程资源吗? *问题2:等待await数据返回交给等待线程再继续向下执行吗? *问题3:向await下一条语句执行的线程,是执行await的线程吗?

我也来谈javascript高级编程之:javascript函数编译过程

前言 题目有点大,其实也就是手痒...跟大家来扯一下javascript编译过程. 那么到底什么是"编译"呢 这个...本人文笔太差,我还是直接举例子吧. 相信玩过js童鞋应该都看过下面这样一个面试题: var a=3; function fn(){ alert(a); function a(){ a=5; } a(); alert(a); } fn(); alert(a); 请问上面的题目执行结果如何呢? 各位童鞋答对了没.没答对?...没关系.别急.下面进一段js科普: 各位童鞋都

ASP.NET Core Web 应用程序系列(四)- ASP.NET Core 异步编程之async await

原文:ASP.NET Core Web 应用程序系列(四)- ASP.NET Core 异步编程之async await PS:异步编程的本质就是新开任务线程来处理. 约定:异步的方法名均以Async结尾. 实际上呢,异步编程就是通过Task.Run()来实现的. 了解线程的人都知道,新开一个线程来处理事务这个很常见,但是在以往是没办法接收线程里面返回的值的.所以这时候就该await出场了,await从字面意思不难理解,就是等待的意思. 执行await的方法必须是async修饰的,并且是Task

javascript中undefined和null的区别详解

一.问题的由来 永远不要直接使用undefined进行变量判断使用字符串"undefined"对变量进行判断 这里,undefined是原始值,在JS中undefined出现只有两种情况,一种是变量未定义.一种是定义了变量,但是没有赋值. 如果这个地方person未定义,那么利用person===undefined全等判断就会报错,person未定义但是如果使用typeof来判断,那么就不会报错了. ep: alert(person == undefined);//报错 person

小编带您Volatile的详解

volatile关键字修饰的共享变量主要有两个特点:1.保证了不同线程访问的内存可见性 2.禁止重排序在说内存可见性和有序性之前,我们有必要看一下Java的内存模型(注意和JVM内存模型的区分)为什么要有java内存模型?首先我们知道内存访问和CPU指令在执行速度上相差非常大,完全不是一个数量级,为了使得java在各个平台上运行的差距减少,哪些搞处理器的大佬就在CPU上加了各种高速缓存,来减少内存操作和CPU指令的执行速度差距.而Java在java层面又进行了一波抽象,java内存模型将内存分为