我处境尴尬,
我正在使用纯JavaScript近3年,我知道JavaScript是单线程语言,
并且您可以使用setInterval
和setTimeout
函数模拟异步执行,
但当我想到他们如何工作时,我无法清楚地理解它。 那么这些函数如何影响执行上下文呢?
我认为在特定时间内只运行代码的一部分并在切换到之后
另一部分。如果是这样,则会有很多setInterval
或setTimeout
电话会影响表现吗?
答案 0 :(得分:18)
Javascript是单线程的,但浏览器不是。浏览器至少有三个线程:Javascript引擎线程,UI线程和时序线程,其中setTimeout
和setInterval
的时间由时序线程完成。
当调用setTimeout
或setInterval
时,浏览器中的计时器线程开始倒计时,并且当超时时将回调函数放入javascript线程的执行堆栈中。在堆栈中的其他函数完成之前,不执行回调函数。因此,如果在时间到时执行其他耗时的函数,setTimeout
的回调将无法及时完成。
答案 1 :(得分:6)
浏览器具有用于计时器功能的API,就像事件ex的API一样。
'点击'
'滚动'
假设您的应用程序中有以下代码
function listener(){
...
}
setTimeout(listener, 300)
function foo(){
for(var i = 0; i < 10000; i++){
console.log(i)
}
}
foo()
此时根据我们上面编写的代码,我们的调用堆栈看起来像
Call Stack - &gt; FOO
让我们假设foo将需要1来完成它的执行,因为我们已经在代码中定义了1个超时,并且我们在“foo”完成之前运行它,它的执行是在300ms
那会发生什么?
javascript是否会停止执行foo并开始执行setTimeout?
否强>
正如我们已经知道javascript是单线程的所以它必须在继续前进之前完成foo的执行,但是浏览器如何确保在执行foo之后“setTimeout”会执行?
这里javascript魔法进入画面
当300ms过期时,浏览器“Timer API”启动并将超时处理程序置于“Message Queue”
此时上图中的“Message Queue”看起来像
消息队列 - &gt; setTimout:听者
并且
Call Stack - &gt; FOO
当“Call Stack”变为空时,foo完成它的执行时,如图所示的“Event Loop”将从消息队列中取出消息并将其推入堆栈
“事件循环”的唯一工作是“调用堆栈”变为空并且“消息队列”中有条目然后将消息从“消息队列”出列并将其推入“调用堆栈”
此时上面图片中的Message Queue看起来像
消息队列 - &gt;
并且
Call Stack - &gt;听者
这就是setTimeout和setInterval如何工作,即使我们在setTimeout中指定300 ms它将在“foo”完成它在这种情况下执行后执行,即在1s之后执行。 这就是为什么setTimeout / setInterval中指定的计时器指示“最短时间”延迟执行功能的原因。
答案 2 :(得分:2)
JavaScript是单线程的,但浏览器不是。
有1个堆栈,其中执行了函数和语句。 有1个队列,其中要执行功能的队列。 有一些Web API可以在特定时间保存该功能,这些事件在事件表的setTimeout和setInterval中定义。
当javascript引擎逐行执行js文件时,如果它找到一行作为语句或函数调用,则会将其加载到堆栈上并执行,但如果是setTimeout或setInterval调用, 然后与setTimeout或setInterval关联的函数处理程序由TIME API(浏览器的Web API之一)取出并保留该时间。
这个时间结束后,Time Api将该函数放在执行队列的末尾。
现在该函数的执行取决于队列中提前的其他函数调用。
注意:此函数调用是在窗口对象上调用的。
setTimeout(function () {console.log(this)}, 300)
Window {postMessage:ƒ,blur:ƒ,focus:ƒ,close:ƒ,frames:Window,...}
答案 3 :(得分:1)
JavaScript是一种单线程脚本语言,因此它可以一次执行一段代码(由于其单线程性质),这些代码块中的每一块都“阻塞”其他异步事件的进度。这意味着当发生异步事件时(如鼠标单击,计时器触发或XMLHttpRequest完成),它将排队等待稍后执行。
<强>的setTimeout()强> 当你使用setTimeout()时,它只会在轮到队列时才会执行,如果由于某种原因,setTimeout之前的事件(setTimeout)会阻塞,setTimeout可能比setTimeout()函数中的指定时间延迟。在执行setTimeout回调函数期间,如果发生任何事件(例如点击事件),它将排队等待稍后执行。
setTimeout(function(){
/* Some long block of code... */
setTimeout(arguments.callee, 10);
}, 10);
setInterval(function(){
/* Some long block of code... */
}, 10);
<强>的setInterval()强>
与setTimeout类似,但不断调用该函数(使用 (每次延迟)直到取消。
setTimeout代码将始终至少有10ms的延迟 以前的回调执行(它可能最终会更多,但永远不会 less)而setInterval将尝试执行回调 无论上次回调何时执行,每10毫秒一次。
如果计时器被阻止立即执行,则会延迟 直到下一个可能的执行点(将比...更长) 期望的延迟)。间隔可以无延迟地背靠背执行 如果他们花了足够长的时间来执行(超过指定的时间) 延迟)。
答案 4 :(得分:0)
关于我的经验,请注意,如果要使用setTimeout(),则该函数将始终被延迟(我的意思是稍后执行),或者在没有“超时”的其他代码之后执行。即使事件延迟引起的延迟= 0的函数也可能发生这种情况。
请参见以下示例:
setTimeout(function() {
console.log('Second');
}, 0)
console.log('First');