深入学习setTimeOut
学习 深入 setTimeout
2023-09-14 09:03:56 时间
<html>
<script>
var timeoutTimes = 0;
function timeout() {
timeoutTimes++;
console.log("count: " + timeoutTimes);
if (timeoutTimes < 10) {
setTimeout(timeout, 500);
}
}
// timeout();
var intervalTimes = 0;
function interval() {
intervalTimes++;
console.log("count: " + intervalTimes);
if (intervalTimes >= 10) {
clearInterval(interv);
}
}
// Jerry comment it out on 2017-02-04 10:41PM after dinner 初八
// var interv = setInterval(interval, 500);
</script>
<script>
var start = new Date();
var end = 0;
setTimeout(function() {
console.log(new Date() - start);
}, 500);
/*
可事实却是出乎你的意料,打印结果是这样的(也许你打印出来会不一样,但肯定会大于1000毫秒):
究其原因,这是因为 JavaScript是单线程执行的。也就是说,在任何时间点,有且只有一个线程在运行
JavaScript程序,无法同一时候运行多段代码。
再来看看浏览器下的JavaScript。
浏览器的内核是多线程的,它们在内核控制下相互配合以保持同步,一个浏览器至少实现三个常驻线程:JavaScript引擎线程,GUI渲染线程,浏览器事件触发线程。
● JavaScript引擎是基于事件驱动单线程执行的,JavaScript引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JavaScript线程在运行JavaScript程序。
● GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。但需要注意,GUI渲染线程与JavaScript引擎是互斥的,当JavaScript引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JavaScript引擎空闲时立即被执行。
● 事件触发线程,当一个事件被触发时,该线程会把事件添加到待处理队列的队尾,等待JavaScript引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeout、也可来自浏览器内核的其他线程如鼠标点击、Ajax异步请求等,但由于JavaScript的单线程关系,所有这些事件都得排队等待JavaScript引擎处理(当线程中没有执行任何同步代码的前提下才会执行异步代码)。
到这里,我们再来回顾一下最初的例子:
var start = new Date();
var end = 0;
setTimeout(function() {
console.log(new Date() - start);
}, 500);
while (new Date() - start <= 1000) {}
虽然setTimeout的延时时间是500毫秒,可是由于while循环的存在,只有当间隔时间大于1000毫秒时,才会跳出while循环,也就是说,在1000毫秒之前,while循环都在占据着JavaScript线程。也就是说,只有等待跳出while后,线程才会空闲下来,才会去执行之前定义的setTimeout。
最后 ,我们可以总结出,setTimeout只能保证在指定的时间后将任务(需要执行的函数)插入任务队列中等候,但是不保证这个任务在什么时候执行。一旦执行javascript的线程空闲出来,自行从队列中取出任务然后执行它。
因为javascript线程并没有因为什么耗时操作而阻塞,所以可以很快地取出排队队列中的任务然后执行它,也是这种队列机制,给我们制造一个异步执行的假象。
*/
while (new Date() - start <= 1000) {}
</script>
<script>
/*
本意是立刻执行调用函数,但事实上,上面的代码并不是立即执行的,这是因为setTimeout有一个最小执行时间,当指定的时间小于该时间时,浏览器会用最小允许的时间作为setTimeout的时间间隔,也就是说即使我们把setTimeout的延迟时间设置为0,被调用的程序也没有马上启动。
不同的浏览器实际情况不同,IE8和更早的IE的时间精确度是15.6ms。不过,随着HTML5的出现,在高级版本的浏览器(Chrome、ie9+等),定义的最小时间间隔是不得低于4毫秒,如果低于这个值,就会自动增加,并且在2010年及之后发布的浏览器中采取一致。
所以说,当我们写为 setTimeout(fn,0) 的时候,实际是实现插队操作,要求浏览器“尽可能快”的进行回调,但是实际能多快就完全取决于浏览器了。
那setTimeout(fn, 0)有什么用处呢?其实用处就在于我们可以改变任务的执行顺序!因为浏览器会在执行完当前任务队列中的任务,再执行setTimeout队列中积累的的任务。
通过设置任务在延迟到0s后执行,就能改变任务执行的先后顺序,延迟该任务发生,使之异步执行。
当你往两个表单输入内容时,你会发现未使用setTimeout函数的只会获取到输入前的内容,而使用setTimeout函数的则会获取到输入的内容。
这是为什么呢?
因为当按下按键的时候,JavaScript 引擎需要执行 keydown 的事件处理程序,然后更新文本框的 value 值,这两个任务也需要按顺序来,事件处理程序执行时,更新 value值(是在keypress后)的任务则进入队列等待,所以我们在 keydown 的事件处理程序里是无法得到更新后的value的,而利用 setTimeout(fn, 0),我们把取 value 的操作放入队列,放在更新 value 值以后,这样便可获取出文本框的值。
未使用setTimeout函数,执行顺序是:onkeydown => onkeypress => onkeyup
使用setTimeout函数,执行顺序是:onkeydown => onkeypress => function => onkeyup
虽然我们可以使用keyup来替代keydown,不过有一些问题,那就是长按时,keyup并不会触发。
长按时,keydown、keypress、keyup的调用顺序:
keydown
keypress
keydown
keypress
...
keyup
也就是说keyup只会触发一次,所以你无法用keyup来实时获取值。
*/
function onloadfun(){
document.querySelector('#one').onkeydown = function() {
document.querySelector('#one1').innerHTML = this.value;
};
document.querySelector('#second').onkeydown = function() {
setTimeout(function() {
document.querySelector('#second1').innerHTML = document.querySelector('#second').value; }, 0);
};
}
/*
setTimeout不止两个参数
我们都知道,setTimeout的第一个参数是要执行的回调函数,第二个参数是延迟时间(如果省略,会由浏览器自动设置。在IE,FireFox中,第一次配可能给个很大的数字,100ms上下,往后会缩小到最小时间间隔,Safari,chrome,opera则多为10ms上下。)
其实,setTimeout可以传入第三个参数、第四个参数….,它们表示神马呢?其实是用来表示第一个参数(回调函数)传入的参数。
setTimeout(function(a, b){
console.log(a); // 3
console.log(b); // 4
},0, 3, 4);
*/
</script>
<script>
/*
Jerry 2017-03-17 16:58PM */
var timerStart1 = now();
setTimeout(function () {
console.log('第一个setTimeout从call SetTimeout到真正被执行:', now() - timerStart1);
var timerStart2 = now();
setTimeout(function () {
console.log('第二个setTimeout从call SetTimeout到真正被执行:', now() - timerStart2);
}, 100);
heavyTask(3123); // 耗时任务
}, 100);
var loopStart = now();
heavyTask(1781); // 耗时任务
console.log('heavyTask耗费时间:', now() - loopStart);
function heavyTask(duration) {
var s = now();
while(now() - s < duration) {
}
}
function now () {
return new Date();
}
</script>
<body onload="onloadfun()">
<p>one</p>
<input id="one"></input>
<span id="one1"></span>
<p>two</p>
<input id="second"></input>
<span id="second1"></span>
</body>
</html>
相关文章
- 录屏|面向气候变化研究的机器学习
- Spring Framework 源码学习笔记(五)
- Sass学习(三)--函数
- 【Linux学习随笔】三、shell语法-变量「建议收藏」
- Databus 深入学习
- servlet学习笔记(入门)
- 机器学习信仰之决策树详解大数据
- Java学习笔记之二十八深入了解Java8新特性详解编程语言
- 深入学习 Java 线程池详解编程语言
- 老男孩学习Linux运维:坚持不懈的探索之旅(老男孩学linux运维)
- MySQL自增2:深入学习自增长的机制(mysql自增2)
- 厦门深入学习Linux培训班(厦门linux培训)
- Linux深入学习之旅:CDT篇(linuxcdt)
- Oracle数据库的有效使用方法:必要的学习与实践(oracle的使用方法)
- 深入学习Oracle的触发器类型(oracle触发器类型)
- 程序员如何学习编码
- 深入学习!Linux下OSG编译完整教程分享!(linuxosg编译)
- 深入学习Linux中的LFTP(linuxlftp)
- 学习Oracle: 如何创建表空间(oracle 表空间创建)
- 深入学习Linux C:掌握函数参考指南(linuxc函数参考)
- 深入Linux:学习Linux语言(查看 linux 语言)
- MySQL学习攻克2万多项掌握(2万mysql笔记)
- 深入学习Oracle实战型代码示例分享(oracle代码示例)
- Hinton 机器学习教程 第一课汉化版完整放送!