原来你是这样的setTimeout
先上代码
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的更多相关文章
- setTimeout 的黑魔法
setTimeout,前端工程师必定会打交道的一个函数.它看上去非常的简单,朴实.有着一个很不平凡的名字--定时器.让年少的我天真的以为自己可以操纵未来.却不知朴实之中隐含着惊天大密.我还记得我第一次 ...
- 你所不知道的setTimeout
JavaScript提供定时执行代码的功能,叫做定时器(timer),主要由setTimeout()和setInterval()这两个函数来完成.它们向任务队列添加定时任务.初始接触它的人都觉得好简单 ...
- setTimeout那些事儿
一.setTimeout那些事儿之单线程 一直以来,大家都在说Javascript是单线程,浏览器无论在什么时候,都且只有一个线程在运行JavaScript程序. 但是,不知道大家有疑问没——就是我们 ...
- 深入理解定时器系列第一篇——理解setTimeout和setInterval
× 目录 [1]setTimeout [2]setInterval [3]运行机制[4]作用[5]应用 前面的话 很长时间以来,定时器一直是javascript动画的核心技术.但是,关于定时器,人们通 ...
- 前端开发:setTimeout与setInterval 定时器与异步循环数组
前端开发:setTimeout与setInterval 定时器与异步循环数组 前言: 开通博客园三个月以来,随笔记录了工作中遇到的大大小小的难题,也看过无数篇令人启发的文章,我觉得这样的环境是极好的, ...
- javascript函数setInterval和setTimeout的使用区别详解
setTimeout和setInterval的使用 这两个方法都可以用来实现在一个固定时间段之后去执行JavaScript.不过两者各有各的应用场景. 方 法 实际上,setTimeout和setIn ...
- UNITY实现FLASH中的setTimeout
setTimeout是一个很方便的DELAY处理方法 if (this.startUpDelay > 0){ StartCoroutine(DelayedStart()); ...
- setTimeout和setInterval从入门到精通
我们在日常web前端开发中,经常需要用到定时器方法. 前端中的定时器方法是浏览器提供的,并不是ECMAScript规范中的.是window对象的方法. 浏览器中的定时器有两种, 一种是每间隔一定时间执 ...
- setTimeout和setInterval定时器使用详解测试
var len=4; while(len--){ var time=setTimeout(function(){ console.log(len); },0); console.log(time); ...
- setTimeout 和 throttle 那些事儿
document.querySelector('#settimeout').onclick= function () { setTimeout(function () { console.log('t ...
随机推荐
- PHP 2个比较经典的加密解密函数
这篇文章主要介绍了2个比较经典的PHP加密解密函数分享,一个是Discuz!的authcode加密函数(带详细分解),一个是encrypt()函数,都比较经典,需要的朋友可以参考下 项目中有时我们需要 ...
- 面向对象编程思想(OOP)
本文我将从面向对象编程思想是如何解决软件开发中各种疑难问题的角度,来讲述我们面向对象编程思想的理解,梳理面向对象四大基本特性.七大设计原则和23种设计模式之间的关系. 软件开发中疑难问题: 软件复杂庞 ...
- 正则表达式,grep,sed,
答案详见:http://www.cnblogs.com/linhaifeng/p/6596660.html 作业一:整理正则表达式博客 ^ # 行首定位 $ # 行尾定位 . # 匹配除换行符以外的任 ...
- 复杂xml格式报文和实体类之间的转化
pom.xml中引入如下依赖: <dependency> <groupId>org.eclipse.persistence</groupId> <artifa ...
- 13. Roman to Integer ★
题目内容: Given a roman numeral, convert it to an integer. Input is guaranteed to be within the range fr ...
- mybatis中<foreach>标签的使用
标签太多,记一下不是特别常用的标签~留着脑袋瓜不机灵的时候看! <foreach>标签 该标签的作用是遍历集合类型的条件 <select id="countByUserL ...
- OO第一单元作业总结之初识面向对象
第一个单元的三次作业均为求导,循序渐进的让我们掌握如何构造类和方法,让整个代码是面向对象的设计而不是面向过程的设计.如果第一次作业和第二次作业你只是简单的对过程着手架构类,到了第三次作业就会变得格外麻 ...
- intellij idea 程序包不可见问题
刚用intellij idea不久,出现了个程序包不可见的问题. 原因:jdk9的问题,用的是jdk9就会出现这个问题. 解决办法:把jsk9换成jdk1.8就能解决.
- find命令配合sed命令使用
1.查找当前目录下所有以txt文件中包含123的数字都替换成5678 find ./ -name "*.txt" -exec grep "123" {} \; ...
- 关于MySQL什么时候使用索引问题以及什么情况下应不建或少建索引
一,什么情况下使用索引1. 表的主关键字 自动建立唯一索引 2. 表的字段唯一约束 ORACLE利用索引来保证数据的完整性 3. 直接条件查询的字段 在SQL中用于条件约束的字段 如zl_yhjbqk ...