先上代码

 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. springboot学习小记

    思维导图:https://www.edrawsoft.cn/viewer/public/s/72a06689197636 1.springboot是一个快速整合第三方框架,简化XML配置完全采用注解化 ...

  2. 将一个js项目改造成vue项目

    本地环境:node版本:8.11.4 vue版本:3.4.1; 开发工具vscode 1.创建一个空的vue项目(vue create bigdata_reprot_web) 2.找到项目的空白页,改 ...

  3. web中的请求:get  与 post

    web中get与post请求的区别:1. get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到.post是通过HTTP post机制,将表 ...

  4. python修炼第七天

    第七天面向对象进阶,面向对象编程理解还是有些难度的,但是我觉得如果弄明白了,要比函数编程过程编程省事多了.继续努力! 1.面向对象补充: 封装 广义上的封装:把变量和函数都放在类中狭义上的封装:把一些 ...

  5. ubuntu16.04安装anaconda、环境配置

    anaconda默认3.7降级到3.6 conda install python=3.6 anaconda安装后找不到conda命令: 执行测试命令 conda info -e conda: comm ...

  6. Forth 文本解释程序

    body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...

  7. Java并发编程阅读笔记-同步容器、工具类整理

  8. while循环与 for循环

    import turtle turtle.setup(600,400,0,0) turtle.bgcolor('red') turtle.color('yellow') turtle.fillcolo ...

  9. 简单的OO ALV小示例

    OO ALV即面向对象ALV,是在屏幕上显示的一种可以自定义大小的ALV.它区别普通ALV的优点是可以多个ALV出现在同一个屏幕,也可以同其他屏幕元素同时出现在同一个屏幕. 示例展示: 1. 进入实用 ...

  10. 待实验的socketserver

    # -*- coding:utf-8 -*-# Author: Dennis Huang__Author__ = "Dennis" import socketserver clas ...