Javascript异步编程之setTimeout与setInterval

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

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

      2. 分布式事件(pub/sub).

      3. Promise对象和Deferred对象。

接下来这篇博客会总结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函数和计时函数。

一:异步的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这么一个延迟。也就是说使用这个计时不是非常精确。

Javascript异步编程之setTimeout与setInterval详解分析(一)的更多相关文章

  1. 【转】Javascript异步编程之setTimeout与setInterval

    Javascript异步编程之setTimeout与setInterval 转自:http://www.tuicool.com/articles/Ebueua 在谈到异步编程时,本人最主要会从以下三个 ...

  2. 三、VIP课程:并发编程专题->01-并发编程之Executor线程池详解

    01-并发编程之Executor线程池详解 线程:什么是线程&多线程 线程:线程是进程的一个实体,是 CPU 调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系 ...

  3. 有关定时器setTimeout()、setInterval()详解

    JavaScript提供定时执行代码的功能,叫做定时器(timer),主要由setTimeout()和setInterval()这两个函数来完成. setTimeout() setTimeout函数用 ...

  4. setTimeOut和setInterval详解

    setTimeout和setInterval的语法相同.它们都有两个参数,一个是将要执行的代码字符串,还有一个是以毫秒为单位的时间间隔,当过了那个时间段之后就将执行那段代码.不过这两个函数还是有区别的 ...

  5. javascript异步编程之generator(生成器函数)与asnyc/await语法糖

    Generator 异步方案 相比于传统回调函数的方式处理异步调用,Promise最大的优势就是可以链式调用解决回调嵌套的问题.但是这样写依然会有大量的回调函数,虽然他们之间没有嵌套,但是还是没有达到 ...

  6. Javascript 异步编程的4种方法详解

    你可能知道,Javascript语言的执行环境是"单线程"(single thread). 所谓"单线程",就是指一次只能完成一件任务.如果有多个任务,就必须排 ...

  7. unix网络编程之listen()详解

    转自于:http://blog.csdn.net/ordeder/article/details/21551567 Unix网络编程描述如下: #include <sys/socket.h> ...

  8. 异步编程之Generator(1)——领略魅力

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  9. (翻译)异步编程之Promise(1):初见魅力

    原文:https://www.promisejs.org/ by Forbes Lindesay 异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2) ...

随机推荐

  1. 新建hadoop用户以及用户组,给予sudo权限

    1.首先新建用户,adduser命令 sudo adduser hadoop passwd hadoop 输入密码之后,一路 y 确定. 2.添加用户组 在创建hadoop用户的同时也创建了hadoo ...

  2. python学习之老男孩python全栈第九期_day004知识点总结

    1. 列表list: 列表转换成字符串: s = 'kidd' s1 = '_'.join(s) # 用_连接 字符串转换成列表: split() range(头,尾,步长): [0,1,2,3,4, ...

  3. 大数据java基础day01

    day01笔记 1.==操作符和equals方法 equals方法存在于Object类中,所有类的equals方法都继承于Object 2.String类的常用方法 ①.replace()替换字符串 ...

  4. requireJS基本概念及使用流程(2)

    上一篇我们一起研究了研究requireJS,这一篇我们来说一说requireJS具体的使用过程 其实很简单的,我总结了总结就是分为四步走 第一步:在页面中引入requireJS并且引入入口文件 第二步 ...

  5. 高德地图 JS API - 根据地名实现标记定位

    德地图 JS API 使用前的准备工作请参考官方网站说明: https://lbs.amap.com/api/javascript-api/guide/abc/prepare 根据地名实现地图标记定位 ...

  6. sql server 大批数据插入时,时间过长的问题

    private const string con = "server=192.168.30.36;database=test;user=sa;pwd=123456"; static ...

  7. ActiveReports 报表控件V12新特性 -- 可定制的安装设置

    ActiveReports是一款专注于 .NET 平台的报表控件,全面满足 HTML5 / WinForms / ASP.NET / ASP.NET MVC / WPF 等平台下报表设计和开发工作需求 ...

  8. mui中图片手势缩放功能的实现

    MUI框架,要实现手势缩放图片,可以使用imageviewer组件来实现.代码很简单: 引入css: <link href="assets/css/mui.imageviewer.cs ...

  9. 【LLVM笔记】0x00 初识LLVM 链接类型

    模块结构 LLVM程序是由若干的模块(Module)组成,每个模块中包含有一些函数.全局变量和符号表. 这些模块可能由LLVM的连接器组合在一起,组合的过程将会整合这些函数和全局变量的定义,整合他们的 ...

  10. 字节顺序标记BOM

    最近,从numbers导出的csv文件,导入excel后,出现中文乱码问题.网上查询后,发现是numbers导出的csv默认是utf-8无BOM的,使用sublimText3打开,另存为utf-8wi ...