JavaScript的setInterval会阻塞线程执行吗?
JavaScript的setInterval会阻塞线程执行吗?
setInterval会导致页面中的其他脚本阻塞吗?
我正在将一个与Gmail相关的书签脚本转换成Google Chrome扩展的项目中。该书签脚本使用Gmail Greasemonkey API与Gmail页面交互。 API的JavaScript对象是Gmail页面加载的最后部分之一,并通过XMLHttpRequest加载到页面中。由于我需要访问此对象,而全局JavaScript变量在扩展内容脚本中是隐藏的,所以我将一个脚本注入到Gmail页面中,以轮询变量的定义并访问它。我使用setInterval函数进行轮询。这个方法大约有80%的成功率。其余的时间,轮询函数会一直轮询直到达到我设定的限制,而greasemonkey API对象在页面中从未定义。
注入的脚本示例:
var attemptCount = 0; var attemptLoad = function(callback) { if (typeof(gmonkey) != "undefined"){ clearInterval(intervalId); // 取消注册此函数的间隔执行。 gmonkey.load('1.0', function (gmail) { self.gmail = gmail; if (callback) { callback(); } }); } else { attemptCount ++; console.log("Gmonkey尚未加载:" + attemptCount ); if (attemptCount > 30) { console.log("在三十秒内无法在gmail页面中找到Gmonkey。中止"); clearInterval(intervalId); // 取消注册此函数的间隔执行。 }; } }; var intervalId = setInterval(function(){attemptLoad(callback);}, 1000);
JavaScript是单线程的,除了我们这里不讨论的Web Workers。这意味着只要常规的JavaScript线程执行,你的setInterval()定时器就不会运行,直到常规的JavaScript线程执行完毕。
同样地,如果你的setInterval()处理程序正在执行,其他JavaScript事件处理程序也不会触发,直到你的setInterval()处理程序完成当前的调用。
因此,只要你的setInterval()处理程序不被卡住并一直运行,它就不会阻塞其他事物的运行。它可能会稍微延迟它们的运行,但它们将在当前的setInterval()线程完成后立即运行。
在内部,JavaScript引擎使用一个队列。当某个东西想要运行(比如事件处理程序或setInterval()回调)并且已经有东西在运行时,它会将一个事件插入队列中。当当前的JavaScript线程执行完毕时,JS引擎会检查事件队列,如果有东西存在,它会选择最旧的事件并调用它的事件处理程序。
以下是关于JavaScript事件系统工作原理的其他一些参考资料:
谢谢。我明白了,在处理程序运行时其他脚本无法运行,但我期望处理程序的执行非常简短,然后控制权会被交还给其他需要运行的脚本/事件,直到1秒的时间间隔过去,处理程序再次被调用。我在这方面有什么遗漏吗?
- 这是正确的解释。如果你的定时器脚本只运行很短的时间(比如几毫秒),那么在1秒的时间间隔中,剩余的时间都可以用于其他所有事件处理程序或定时器的运行。
这一定是stackoverflow上关于setInterval()的最好解释。
JavaScript的setInterval和setTimeout是“礼貌”的,因为它们不会在你期望的时间触发,而是在线程空闲后的任何时间触发,即在你指定的时间点之后。
因此,调度某个操作不会阻止其他操作的执行,它只是在当前队列的末尾或指定的时间结束时设置自己运行。
有两个重要的注意事项:
第一个注意事项是setTimeout和setInterval有特定于浏览器的最小间隔。通常,它们的最小间隔约为15毫秒。因此,如果你每1毫秒请求一次操作,浏览器实际上会将它们安排在间隔为browser_min_ms的时间间隔内(browser_min_ms不是真实变量)。
第二个注意事项是,如果setInterval中的脚本执行时间超过间隔时间,就会遇到一个问题,浏览器将不断排队积压间隔操作。
function doExpensiveOperation () { var i = 0, l = 20000000; for (; i < l; i++) { doDOMStuffTheWrongWay(i); } } setInterval(doExpensiveOperation, 10);
BadTimes+=1;
但是对于你的代码,你所做的事情本质上没有问题。
正如我之前说的,setInterval不会中断其他操作的执行,它只会插入到下一个可用时间槽中。
我可能会建议你在一般情况下使用setTimeout,但你仍然很好地控制了间隔并使其间隔开。
可能在代码的其他地方,或者在Google的交付中,或者在你的集合中还有其他问题。