Home > Article > Web Front-end > Interview: What exactly is setTimeout in JavaScript?
The content of this article is about interviews: What is setTimeout in JavaScript? , has certain reference value, friends in need can refer to it, I hope it will be helpful to you.
When writing test questions, I often encounter the problem of setTimeout. I only know that this is to set a timer; but the focus of the investigation is generally to include a timer in a method. The timer Maybe what I said is a bit difficult to understand about the printing and execution order of printing in the method. Let’s take a look at what setTimeout is!
Introduction to timers
What are the timers in js?
Periodic timer: setInterval()
Introduction
setInterval() is to call the timer according to the specified period , the method will continuously call the timer until it is stopped using clearInterval() or the window is closed.
Syntax
setInterval(code,millisec,lang)
code: The method body to be executed (required)
millisec: Execute every millisecond (unit is millisecond, if set to 5000, it will be executed every 5 seconds) (required)
lang: refers to the language used (optional)
Example
Achieve clock effect through setInterval
<input> <script> //每隔1秒执行一次clock方法 var int=self.setInterval("clock()",1000); function clock() { var d=new Date(); var t=d.toLocaleTimeString(); document.getElementById("clock").value=t; } </script> <!-- 设置一个按钮,点击按钮即停止定时器 --> <button>停止</button>
Rendering:
As the name suggests, this timer will only be executed once. The difference from setInterval() is here. It is precisely because of this that setInterval() needs to use the clearInterval method to cancel the timer
setTimeout(code,millisec,lang) ps: The meaning of each parameter is the same as that of setInterval()
Click the button and "Hello" will pop up after 3 seconds
nbsp;html> <meta> <title>菜鸟教程(runoob.com)</title> <p>点击按钮,在等待 3 秒后弹出 "Hello"。</p> <button>点我</button> <script> function myFunction() { setTimeout(function(){alert("Hello")},3000); } </script>
Rendering:
//设置超时调用 var timeoutId = setTimeout(function (){ alert("hello World"); },1000); //取消掉用的代码 clearTimeout(timeoutId);
There is a js engine thread in the rendering process. This thread is used to process javaScript scripts (such as chrome's V8 engine), and we have always said that javaScript is single-threaded because of this.
So the question is, since js is single-threaded, how is the asynchronous setTimeout implemented? When js parses a script, it will divide tasks into two categories, synchronous tasks and asynchronous tasks, which will enter different places for execution during parsing.
Maybe you are familiar with the process of the event loop mechanism I still don’t quite understand, so let me explain it a little more clearly. For example, the following example:
console.log('start') setTimeout(function(){ console.log('setTimeout') },5000) console.log('end')
Execution process:
start;
end;
5s后,将回调函数放进Event Queue,此时执行栈刚好为空,主线程会去任务队列中取出这个回调函数,执行,打印setTimeout
ps:
第1,3步都是js引擎线程干的事情,主线程执行任务;
第2步是渲染进程中的事件触发线程(专门管理任务队列的)管理;
第4步是定时器线程控制的(也就是setTiemout和setInterval所在的进程),定时器线程专门用来控制什么时候将回调函数放进任务队列。
如果看懂了上面的例子,就知道其实setTimeout的第二个参数其实并不能准确的控制多少秒后执行里面的函数,而是控制多少秒后将这个函数放进任务队列中;这样也就同样可以解释,为什么有时候明明设置的是2秒之后执行,却要等不止2秒(因为很有可能定时线程将回调函数放进任务队列后,主线程还在执行执行栈中的任务,需要执行栈中的任务全部执行完后才会去任务队列中取任务)。
这样就会引发一个问题,我们知道setInterval是隔一定的时间执行一次,现在理解了原理后,就知道其实是隔一定的时间定时器线程将回调函数放进任务队列中。如果已经将回调函数放进任务队列,但是主线程正在执行一个非常耗时的任务,当这个任务执行完毕后,主线程去任务队列中取任务,这个时候,就会出现连续执行的情况,也就是说setInterval相当于失效了。
这一部分主要是针对在事件循环机制中setTimeout调顺序进行举例子,如果能够轻松的将例子看懂,就说明你是真的懂了事件循环机制的一部分,为什么说是一部分呢,因为还有一个宏任务和微任务的知识点还没有涉及到,后面的进阶篇就会涉及到啦!
console.log('start') setTimeout(function(){ console.log('setTimeout') },0) console.log('end')
打印结果:(如果前面看懂了的同学应该就会明白为什么)
分析:其实和上面那个例子时一样的,只是这个0会给我们一种会立即执行的假象,这个0是说明定时器线程会立即将回调函数放进任务队列而已,主线程还是会将执行栈中的两个同步任务执行完成后再去任务队列中取任务,所以执行顺序和这里的秒数无关。而且即使执行栈为空,也不会0秒就执行,因为HTML的标准规定,setTimeout不超过4ms按照4ms来计算。
console.log('start') setTimeout(function(){ console.log('setTimeout') }(),0) console.log('end')
打印结果:(仔细对比与例1的区别)
分析:细心的同学会发现,我将回调函数改成了立即执行函数,就改变了执行的顺序。首先我们需要明确的是setTimeout的第一个参数是指函数的返回值,这里回调函数为立即执行函数时,返回值就是undefined了,所以会直接执行立即执行函数,也就是立即打印setTimeout,而真正的setTimeout函数就相当于没起作用。
setTimeout(() => { console.log('setTimeout') },3000) sleep(10000000)//伪代码,表示这个函数要执行很久很久
打印结果:
这个结果不说也知道,肯定会打印出setTimeout的,但是重点却不在这儿~
重点在于,这个setTimeout是隔很久很久打印出来的,远远超过了3秒,这个例子也是很明确的体现了js的事件循环机制。
这一部分相对于基础篇,加上了作用域以及其他也是比较难以理解的东西,可能还需要补充一些其他知识才会明白,我会尽量讲清楚,也会把我看的参考文章放在下面。
受到一篇文章的启发,我们以循序渐进的方式来阐述
问题:以下代码输出的是什么?
for(var i = 0;i <p><strong>答案:</strong>没错,你没有看错,就是一个简单的循环,就像你想的那样,连续输出0,1,2,3,4</p><h3>难度:OO</h3><p><strong>问题:</strong>以下代码输出的是什么?如果把时间改为1000*i输出的又是什么?</p><pre class="brush:php;toolbar:false">for(var i = 0;i <p><strong>答案:</strong><br> 时间为1000时,1秒后会连续输出5个5;时间为1000*i时,会每隔一秒输出一个5,一共5个5<br><strong>分析:</strong><br> 由上面的事件循环机制我们知道,setTimeout是异步事件,会放在事件队列中等着主线程来执行,这个时候for循环中的i已经变成了5,由于定时器线程是在1秒后直接将5个setTimeout事件放进事件队列中,所以主线程在执行的时候就没有间隔了;当时间乘上一个i时,定时器会隔1秒将setTimeout事件放入队列,就会出现每隔一秒输出一个5的情况。</p><h3>难度:OOO</h3><p><strong>问题:</strong>如果想输出0,1,2,3,4应该怎么改?<br><strong>分析:</strong><br> 出现上一题的情况主要是因为在setTimeout的回调函数中并没有保存每次循环i的值,最后执行的时候,得到的i就是最后更新的i了(即为5),所以要解决这个问题,思路是要在回调函数中保存每次for循环中的i值。</p><p><strong>解决方案1:</strong>使用es6中let代替var<br><strong>分析:</strong>let是es6中新增的内容,作用和var一样,都是用来定义变量,但是最大的差别就是let会形成块级作用域,在本例中,就是每次循环,都会产生一个作用域,在该作用域中的变量是一个固定值,下次i变化时不会对这个i产生影响,也就是达到了我们的目标。</p><pre class="brush:php;toolbar:false">for(let i = 0;i <p><strong>解决方案2:</strong>使用闭包<br><strong>分析:</strong>就是直接在setTimeout函数的外面套一层立即执行函数,并将i值作为参数传到匿名函数中(这里的匿名函数也可以是命名函数),然后由于setTimeout中回调函数用到了匿名函数中的i,就会形成闭包。</p><pre class="brush:php;toolbar:false">for(var i = 0;i <p><strong>延伸:</strong>将代码变成下面这样会输出什么?(去掉匿名函数中的i)<br><strong>分析:</strong>这里会输出5个5,也就是闭包没有起作用,根本原因是i并没有传进去,打印的还是最后的i</p><pre class="brush:php;toolbar:false">for (var i = 0; i <p><strong>解决方案3:</strong>将回调函数改成立即执行函数<br><strong>分析:</strong>这个解决方案其实不是太好,如果要求是每隔1秒输出一个数字,这个方法就不适用了;这个方法会立马输出0,1,2,3,4,原因结合基础篇应该就明白了</p><pre class="brush:php;toolbar:false">for (var i = 0; i <h3>难度:OOOO</h3><p> 这一部分会涉及到promise,事件循环机制,宏任务和微任务的内容,算是比较难的部分了,如果觉得比较难看懂,最好先去补一下基础知识,我这里就简单介绍一下。</p><h4>promise对象</h4><p>我这里就不详细讲了。</p><h4>宏任务和微任务</h4>
宏任务:可以理解成将代码块走一遍的过程,setTimeout和promise都是宏任务,现在不理解没关系,后面会通过例子帮助理解
微任务:是在宏任务执行完成之后执行的,也是有相应的微任务队列存放微任务,比如promise中的then就是微任务
问题:以下代码输出的是什么?
setTimeout(function () { console.log(1) }, 0); new Promise(function executor(resolve) { console.log(2); for (var i = 0; i <p><strong>答案:</strong>(是不是很懵,为什么会是这样,下面看我的分析你就知道了)</p><p style="text-align: center;"><span class="img-wrap"><img src="https://img.php.cn//upload/image/617/114/318/1543217488584753.png" title="1543217488584753.png" alt="Interview: What exactly is setTimeout in JavaScript?"></span></p><p><strong>分析:</strong></p><ol class=" list-paddingleft-2"> <li><p>进入宏任务(从第一行到最后一行执行一遍的过程),碰到setTimeout,将setTimeout放进事件队列中;</p></li> <li><p>碰到promise,执行console,<strong>打印2</strong>;</p></li> <li><p>经过循环后,执行console,<strong>打印3</strong>;</p></li> <li><p>到了then,由于then是微任务,会在宏任务执行完成后执行,放进微任务队列;</p></li> <li><p>遇到console,<strong>打印5</strong>;</p></li> <li><p>至此,第一次的宏任务执行完成,接下来执行微任务队列中的then,<strong>打印4</strong>;</p></li> <li><p>现在执行栈中的任务都执行完了,现在就要去事件队列中取事件,此时执行setTimeout这个宏任务,<strong>打印1</strong>;</p></li> </ol><p><strong>宏任务微任务与同步事件异步事件的关系:</strong><br> 这些词都是用来描述事件的,只是从不同的角度来描述,就像是胖子矮子与男生女生之间的联系</p><p class="comments-box-content"><br></p><p> </p>
The above is the detailed content of Interview: What exactly is setTimeout in JavaScript?. For more information, please follow other related articles on the PHP Chinese website!