先上代码

 console.log("start");
setTimeout(function(){
console.log("Hello");
},200);
setTimeout(function(){
console.log("World");
},100);
console.log("end");

start
end
undefined
World
Hello

如果看出了结果,那么我们修改一下代码

console.log("start");
setTimeout(function(){
console.log("Hello");
},200);
setTimeout(function(){
console.log("World");
},300);
for(var i = 0;i <= 1000; i++){
console.log(i);
}
setTimeout(function(){
console.log("I am here!");
},100);
console.log("end");

start

end

undefined
I am here!
Hello
World

是的,在我们看来这是正确的,没有错误的。

那我们再稍微修改一下我们的代码

console.log("start");
setTimeout(function(){
console.log("Hello");
},200);
setTimeout(function(){
console.log("World");
},300);
for(var i = 0;i <= 10000; i++){
console.log(i);
}
setTimeout(function(){
console.log("I am here!");
},100);
console.log("end"); start

end
undefined
Hello
World
I am here!

是不是很神奇。首先得从JS引擎说起。

JS引擎在内存中会分配堆区和栈区。

我们来看一段代码

 function A(){
var a = 4;
b(a);
}
function B(num){
var newNum = num * num;
console.log(newNum);
}
A();

步骤:运行A()方法,将A()入栈。A上下文中存在变量a = 4

   调用B()方法,将B()入栈。B上下文中存在变量num = 4,newNum = 16

   调用B()方法中的console.log(),入栈,打印出16.

      继续运行,将console.log()、B()方法,出栈,A()方法运行完毕出栈,代码运行完毕,清空栈。

众所周知,JS引擎是单线程的,在某一个特定时间只能执行一个任务,并阻塞其他任务的执行,也就是说这些任务是串行的。用户不得不等待一个耗时的操作完成之后才能继续后面的操作,实际开发中我们可以使用异步代码来解决问题。

EventLoop事件循环

当异步方法比如这里的setTimeout(),或者Ajax请求、DOM事件执行的时候,会交由浏览器内核的其他模块去管理。当异步的方法满足触发条件之后,该模块就将方法推入到一个任务队列(task queue)中,当主线程代码执行完毕处于空闲状态的时候,就会检查任务队列,将队列中的第一个任务入栈执行,完毕后继续检查任务队列,如此循环。

前提条件:主线程处于空闲状态,这就是事件循环的模型。

我们来看一下第一个列子:

首先console.log()入栈,打印完毕之后出栈,紧接着执行到setTImeout()计时器,此时JS引擎会将定时器交给浏览器的另一个模块去管理,在这里我们称Timer()模块,紧接着第二个计时器也交给Timer模块。

然后执行到第二个console.log(),执行完毕后清空执行栈。

但是并没有结束,在主线程执行的同时,Timer()模块会检查其中的代码,一旦满足触发条件,就会将它添加到任务队列中,TImer2延迟100秒,所以在于Timer1被添加到队列开头。而主线程此时处于空闲状态,所以会检查任务队列是否有待执行的任务。

此时会将队列中的Timer2()执行,控制台打印,然后执行栈清空,继续检查任务队列,将Timer1()入栈并执行,控制台打印,清空执行栈。此时任务队列为空,清空执行栈。

然后我们来看第二段和第三段代码,就比较好玩了

和第一段不同的是,在最后一个定时器前加了一个for循环,我们模拟了一个1000和10000的for循环。

第三段比较奇怪。Timer3仅仅延迟了100毫秒,反而在两个Timer()之后执行了。

想想看,为什么?

原因很简单,因为在Timer1和Timer2加入到执行队列中后,主线程仍然执行着for循环中的代码,处于阻塞状态。队列中的Timer1和Timer2并不会得以执行。

当for循环结束,这时才将Timer3交由Timer模块去管理,继续执行后续代码打印"end",清空执行栈。虽然Timer3的延迟时间很短,但是加入任务队列后还是会排在Timer1和Timer2的后面,所以此时会按顺序执行任务队列中的代码。同时需要注意的是,这种情况下的三个定时器延迟执行的时间已经远远超过了指定的时间。

总结:我们发现不论事件循环模型还是setTimeout机制,其实不是难点,但却是容易忽略的点。很多问题的产生是因为忽略了一些简单的原理导致的。

原来你是这样的setTimeout的更多相关文章

  1. setTimeout 的黑魔法

    setTimeout,前端工程师必定会打交道的一个函数.它看上去非常的简单,朴实.有着一个很不平凡的名字--定时器.让年少的我天真的以为自己可以操纵未来.却不知朴实之中隐含着惊天大密.我还记得我第一次 ...

  2. 你所不知道的setTimeout

    JavaScript提供定时执行代码的功能,叫做定时器(timer),主要由setTimeout()和setInterval()这两个函数来完成.它们向任务队列添加定时任务.初始接触它的人都觉得好简单 ...

  3. setTimeout那些事儿

    一.setTimeout那些事儿之单线程 一直以来,大家都在说Javascript是单线程,浏览器无论在什么时候,都且只有一个线程在运行JavaScript程序. 但是,不知道大家有疑问没——就是我们 ...

  4. 深入理解定时器系列第一篇——理解setTimeout和setInterval

    × 目录 [1]setTimeout [2]setInterval [3]运行机制[4]作用[5]应用 前面的话 很长时间以来,定时器一直是javascript动画的核心技术.但是,关于定时器,人们通 ...

  5. 前端开发:setTimeout与setInterval 定时器与异步循环数组

    前端开发:setTimeout与setInterval 定时器与异步循环数组 前言: 开通博客园三个月以来,随笔记录了工作中遇到的大大小小的难题,也看过无数篇令人启发的文章,我觉得这样的环境是极好的, ...

  6. javascript函数setInterval和setTimeout的使用区别详解

    setTimeout和setInterval的使用 这两个方法都可以用来实现在一个固定时间段之后去执行JavaScript.不过两者各有各的应用场景. 方 法 实际上,setTimeout和setIn ...

  7. UNITY实现FLASH中的setTimeout

    setTimeout是一个很方便的DELAY处理方法 if (this.startUpDelay > 0){            StartCoroutine(DelayedStart()); ...

  8. setTimeout和setInterval从入门到精通

    我们在日常web前端开发中,经常需要用到定时器方法. 前端中的定时器方法是浏览器提供的,并不是ECMAScript规范中的.是window对象的方法. 浏览器中的定时器有两种, 一种是每间隔一定时间执 ...

  9. setTimeout和setInterval定时器使用详解测试

    var len=4; while(len--){ var time=setTimeout(function(){ console.log(len); },0); console.log(time); ...

  10. setTimeout 和 throttle 那些事儿

    document.querySelector('#settimeout').onclick= function () { setTimeout(function () { console.log('t ...

随机推荐

  1. 该 URL“XX”无效。它可能指向不存在的文件或文件夹,或者是指向不在当前网站中的有效文件或文件夹

    当使用SharePoint  SPWeb.Files.Add()方法往文档库中写入文件时,报 该 URL“XX”无效.它可能指向不存在的文件或文件夹,或者是指向不在当前网站中的有效文件或文件夹, 原因 ...

  2. event flow

    JS之event flow DOM事件流 1.定义: DOM(文档对象模型)结构是一个树型结构,当一个HTML元素产生一个事件时,该事件会在元素节点与根结点之间的路径传播,路径所经过的结点都会收到该事 ...

  3. 语法、id和class选择器、创建、

    一. 1.CSS规则由两个主要部分构成:选择器,以及一条或多条声明(每条声明由一个属性和一个值构成,属性和值被冒号分开). 2.声明以分号“:”结束,生命组用大括号“{}”括起来. [示例:p {co ...

  4. 使用vagrant构建你们团队的开发环境

    vagrant可以让团队快速搭建统一的开发环境. 搭建vagrant你需要准备三个东西: 1.vagrant安装包 . 2.virtualbox安装包. 3.打包后的vagrant虚拟环境镜像 (ln ...

  5. cocoaPods安装爬坑总结

    1.移除现有Ruby默认源 $ gem sources --remove https://rubygems.org/   2.使用新的源 $ gem sources -a https://ruby.t ...

  6. HFun.快速开发平台(四)=》自定义列表实例(请求参数的处理)

    上编自定义列表描述了自定义列表的基本实现功能,本此记录列表的请求过程. 个人比较喜欢对参数进行对象化,方便后续人维护及查看,先上代码: /******************************* ...

  7. 从0到1用eclipse用maven搭建web项目

    1,默认已经搭建了JDK1.5以上,以及eclipseEE版本,和maven. 2,修改maven的本地仓库和镜像,修改本地仓库是为了方便我们管理,maven的默认仓库是在C盘的USER文件夹下,我一 ...

  8. sticky

    最近有点忘了position几个取值的内容,在这里简单总结一下. position的含义是指定位类型,取值类型可以有:static.relative.absolute.fixed.inherit和st ...

  9. .NET MVC+angular导入导出

    cshtml: <form class="form-horizontal" id="form1" role="form" ng-sub ...

  10. windows环境搭建nginx

    1.下载安装nginx 2.启动nginx:点击nginx.exe文件,cmd,进入nginx根目录,执行start nginx 2.修改nginx配置文件nginx.conf 修改配置文件中serv ...