先上代码

 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. Spring Boot:Thymeleaf 使用详解

    Thymeleaf 介绍 简单说,Thymeleaf 是一个跟 Velocity.FreeMarker 类似的模板引擎,它可以完全替代 JSP .相较与其他的模板引擎,它有如下三个极吸引人的特点: 1 ...

  2. keras-yolo3-master

    logs/000/trained_weights_final.h5 放置训练完的权重 keras-yolo3-master Keras/Tensorflow+python+yolo3训练自己的数据集 ...

  3. js 遍历数组

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title> ...

  4. 【Alpha】事后分析

    Alpha阶段终于告一段落,我们的团队也完整经历了从提出设想.用户需求分析,到开发.测试,再到部署上线.推广的流程."葫芦娃不想写代码"团队还是较出色地完成了Alpha阶段的工作, ...

  5. Nginx实现404页面的几种方法

    一个网站项目,肯定是避免不了404页面的,通常使用Nginx作为Web服务器时,有以下集中配置方式,一起来看看. 第一种:Nginx自己的错误页面 Nginx访问一个静态的html 页面,当这个页面没 ...

  6. codeforces 3b之贪心算法

    有货车运量V:有若干物品A 占2单位体积,有若干物品B占1单位体积:相同种类的物品价值不一定一样: 序号按照输入顺序而定:问货车可以拉走最多多少价值的物品,并输出所选物品的序号: 1)常规的解法思路: ...

  7. 多线程之 Runnable接口

    一.多线程实现的第二种方式 1.定义类,实现Runnable接口 2.重写接口中的run方法,要在run方法中定义线程要执行的任务 public class MyRunnableImpl implem ...

  8. JS 清除DOM 中空白元素节点

    HTML中的空白节点会影响整体的HTML的版面排榜 例如: 制作百度首页时,两个input之间的空白节点将本来是要整合在一起的搜索栏硬是把按钮和搜索框分离出现好丑的间隙 这时我们就可以用js清除这个空 ...

  9. net core 随笔

    UseApplicationInsights  这个有用到azure 才有用, 平时没用的话可以去掉. 遥测. 上下文指的是 进程间占有的资源空间. 当一个进程时间片到了或者资缺的时候就会让出CPU ...

  10. SQL-56 获取所有员工的emp_no、部门编号dept_no以及对应的bonus类型btype和recevied,没有分配具体的员工不显示

    获取所有员工的emp_no.部门编号dept_no以及对应的bonus类型btype和recevied,没有分配具体的员工不显示CREATE TABLE `dept_emp` ( `emp_no` i ...