aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/api/windoworworkerglobalscope/settimeout/index.html
diff options
context:
space:
mode:
Diffstat (limited to 'files/zh-cn/web/api/windoworworkerglobalscope/settimeout/index.html')
-rw-r--r--files/zh-cn/web/api/windoworworkerglobalscope/settimeout/index.html476
1 files changed, 476 insertions, 0 deletions
diff --git a/files/zh-cn/web/api/windoworworkerglobalscope/settimeout/index.html b/files/zh-cn/web/api/windoworworkerglobalscope/settimeout/index.html
new file mode 100644
index 0000000000..f9813851f7
--- /dev/null
+++ b/files/zh-cn/web/api/windoworworkerglobalscope/settimeout/index.html
@@ -0,0 +1,476 @@
+---
+title: window.setTimeout
+slug: Web/API/Window/setTimeout
+tags:
+ - Timers
+ - WindowOrWorkerGlobalScope
+ - WindowTimers
+ - setTimeout
+translation_of: Web/API/WindowOrWorkerGlobalScope/setTimeout
+---
+<p>{{APIRef("HTML DOM")}}</p>
+
+<p> {{domxref("WindowOrWorkerGlobalScope")}} 混合的 <strong><code>setTimeout()</code></strong>方法设置一个定时器,该定时器在定时器到期后执行一个函数或指定的一段代码。</p>
+
+<h2 id="Syntax" name="Syntax">语法</h2>
+
+<pre class="syntaxbox notranslate"><code>var<em> </em>timeoutID = <em>scope</em>.setTimeout(<em>function</em>[<em>,</em> <em>delay, arg1, arg2</em>, ...]);
+var timeoutID = <em>scope</em>.setTimeout(function[, <em>delay</em>]);
+var<em> </em>timeoutID = <em>scope</em>.setTimeout(<em>code</em>[, <em>delay</em>]);</code></pre>
+
+<h3 id="参数">参数</h3>
+
+<dl>
+ <dt><code>function</code></dt>
+ <dd>{{jsxref("function")}} 是你想要在到期时间(<code>delay</code>毫秒)之后执行的<a href="https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function" title="en-US/docs/Core_JavaScript_1.5_Reference/Global_Objects/Function">函数</a>。</dd>
+ <dt><code>code</code></dt>
+ <dd>这是一个可选语法,你可以使用字符串而不是{{jsxref("function")}} ,在<code>delay</code>毫秒之后编译和执行字符串 (使用该语法是<strong>不推荐的,</strong> 原因和使用 {{jsxref("Global_Objects/eval", "eval()")}}一样,有安全风险)。</dd>
+ <dt><code>delay</code> {{optional_inline}}</dt>
+ <dd>延迟的毫秒数 (一秒等于1000毫秒),函数的调用会在该延迟之后发生。如果省略该参数,delay取默认值0,意味着“马上”执行,或者尽快执行。不管是哪种情况,实际的延迟时间可能会比期待的(delay毫秒数) 值长,原因请查看{{anch("实际延时比设定值更久的原因:最小延迟时间")}}。</dd>
+ <dt><code>arg1, ..., argN</code> {{optional_inline}}</dt>
+ <dd>附加参数,一旦定时器到期,它们会作为参数传递给{{jsxref("function")}} </dd>
+</dl>
+
+<div class="note">
+<p><strong>备注</strong>:需要注意的是,IE9 及更早的 IE 浏览器不支持向回调函数传递额外参数(第一种语法)。如果你想要在IE中达到同样的功能,你必须使用一种兼容代码 (查看  {{anch("兼容旧环境(polyfill)")}} 一段).</p>
+</div>
+
+<h3 id="Examples" name="Examples">返回值</h3>
+
+<p>返回值<code>timeoutID</code>是一个正整数,表示定时器的编号。这个值可以传递给{{domxref("WindowOrWorkerGlobalScope.clearTimeout","clearTimeout()")}}来取消该定时器。</p>
+
+<p>需要注意的是<code>setTimeout()</code>和{{domxref("WindowOrWorkerGlobalScope.setInterval", "setInterval()")}}共用一个编号池,技术上,<code>clearTimeout()</code>和 {{domxref("WindowOrWorkerGlobalScope.clearInterval", "clearInterval()")}} 可以互换。但是,为了避免混淆,不要混用取消定时函数。</p>
+
+<p>在同一个对象上(一个window或者worker),<code>setTimeout()</code>或者<code>setInterval()</code>在后续的调用不会重用同一个定时器编号。但是不同的对象使用独立的编号池。</p>
+
+<h2 id="例子">例子</h2>
+
+<p>下文的例子在网页中设置了两个简单的按钮,以触发 <code>setTimeout()</code> 和 <code>clearTimeout()</code> 方法:按下第一个按钮会设置一个定时器,定时器在 2s 后显示一个警告对话框,并将此次 setTimeout 的定时器 ID 保存起来,按下第二个按钮可以取消定时器。</p>
+
+<h3 id="HTML" style="line-height: 24px; font-size: 1.71428571428571rem;">HTML</h3>
+
+<pre class="brush: html notranslate">&lt;p&gt;Live Example&lt;/p&gt;
+&lt;button onclick="delayedAlert();"&gt;Show an alert box after two seconds&lt;/button&gt;
+&lt;p&gt;&lt;/p&gt;
+&lt;button onclick="clearAlert();"&gt;Cancel alert before it happens&lt;/button&gt;
+</pre>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 0px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 19px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 38px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 57px; background: 0px 0px;"></div>
+
+<h3 id="JavaScript" style="line-height: 24px; font-size: 1.71428571428571rem;">JavaScript</h3>
+
+<pre class="brush: js notranslate">var timeoutID;
+
+function delayedAlert() {
+ timeoutID = window.setTimeout(slowAlert, 2000);
+}
+
+function slowAlert() {
+ alert('That was really slow!');
+}
+
+function clearAlert() {
+ window.clearTimeout(timeoutID);
+}
+</pre>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 0px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 19px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 38px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 57px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 76px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 95px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 114px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 133px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 152px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 171px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 190px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 209px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 228px; background: 0px 0px;"></div>
+
+<h3 id="结果展示">结果展示</h3>
+
+<p>{{EmbedLiveSample('例子')}}</p>
+
+<p>也可参考 <a href="/en-US/docs/DOM/window.clearTimeout#Example" title="en-US/docs/DOM/window.clearTimeout#Example"><code>clearTimeout()</code> </a>示例.</p>
+
+<h2 id="兼容旧环境(polyfill)">兼容旧环境(polyfill)</h2>
+
+<p>如果你需要向你的回调函数内传递一个或多个参数, 而且还需要兼容那些不支持传递额外参数(不管使用<code>setTimeout()</code> 或者 <code>setInterval()</code>)的浏览器时(比如,IE9及更早的版本), 你可以引入下面的兼容代码来支持向定时器函数传参。将以下代码添加到你代码的最上面:</p>
+
+<pre class="brush: js notranslate">/*\
+|*|
+|*| Polyfill which enables the passage of arbitrary arguments to the
+|*| callback functions of JavaScript timers (HTML5 standard syntax).
+|*|
+|*| https://developer.mozilla.org/en-US/docs/DOM/window.setInterval
+|*|
+|*| Syntax:
+|*| var timeoutID = window.setTimeout(func, delay[, param1, param2, ...]);
+|*| var timeoutID = window.setTimeout(code, delay);
+|*| var intervalID = window.setInterval(func, delay[, param1, param2, ...]);
+|*| var intervalID = window.setInterval(code, delay);
+|*|
+\*/
+
+(function() {
+ setTimeout(function(arg1) {
+ if (arg1 === 'test') {
+ // feature test is passed, no need for polyfill
+ return;
+ }
+ var __nativeST__ = window.setTimeout;
+ window.setTimeout = function(vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */ ) {
+ var aArgs = Array.prototype.slice.call(arguments, 2);
+ return __nativeST__(vCallback instanceof Function ? function() {
+ vCallback.apply(null, aArgs);
+ } : vCallback, nDelay);
+ };
+ }, 0, 'test');
+
+ var interval = setInterval(function(arg1) {
+ clearInterval(interval);
+ if (arg1 === 'test') {
+ // feature test is passed, no need for polyfill
+ return;
+ }
+ var __nativeSI__ = window.setInterval;
+ window.setInterval = function(vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */ ) {
+ var aArgs = Array.prototype.slice.call(arguments, 2);
+ return __nativeSI__(vCallback instanceof Function ? function() {
+ vCallback.apply(null, aArgs);
+ } : vCallback, nDelay);
+ };
+ }, 0, 'test');
+}())</pre>
+
+<h3 id="针对IE的解决方法">针对IE的解决方法</h3>
+
+<p>如果你需要单独的针对IE9及之前浏览器的 hack 写法,你可以使用 JavaScript 条件注释:</p>
+
+<pre class="brush: js notranslate">/*@cc_on
+ // conditional IE &lt; 9 only fix
+ @if (@_jscript_version &lt;= 9)
+ (function(f){
+ window.setTimeout = f(window.setTimeout);
+ window.setInterval = f(window.setInterval);
+ })(function(f){return function(c,t){var a=[].slice.call(arguments,2);return f(function(){c instanceof Function?c.apply(this,a):eval(c)},t)}});
+ @end
+@*/</pre>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 0px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 19px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 38px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 57px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 76px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 95px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 114px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 133px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 152px; background: 0px 0px;"></div>
+
+<p>或者使用更加清晰的 IE HTML 条件注释:</p>
+
+<pre class="brush: html notranslate">&lt;!--[if lte IE 9]&gt;&lt;script&gt;
+(function(f){
+window.setTimeout=f(window.setTimeout);
+window.setInterval=f(window.setInterval);
+})(function(f){return function(c,t){
+var a=[].slice.call(arguments,2);return f(function(){c instanceof Function?c.apply(this,a):eval(c)},t)}
+});
+&lt;/script&gt;&lt;![endif]--&gt;
+
+
+</pre>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 114px; background: 0px 0px;"></div>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 133px; background: 0px 0px;"></div>
+
+<h3 id="变通方法">变通方法</h3>
+
+<p>另一种方法是使用匿名函数包裹你的回调函数,这种方式要消耗更多资源:</p>
+
+<pre class="brush: js notranslate">var intervalID = setTimeout(function() { myFunc('one', 'two', 'three'); }, 1000);
+</pre>
+
+<div class="line-number" style="margin-top: 1em; position: absolute; left: 0px; right: 0px; line-height: inherit; top: 0px; background: 0px 0px;"></div>
+
+<p>上面那个例子也可以用<a href="/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions">箭头函数</a>:</p>
+
+<pre class="brush: js notranslate">var intervalID = setTimeout(() =&gt; { myFunc('one', 'two', 'three'); }, 1000);
+</pre>
+
+<p>此外,也可使用 <a href="https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind" title="/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind">function's bind</a>:</p>
+
+<pre class="brush: js notranslate">setTimeout(function(arg1){}.bind(undefined, 10), 1000);
+</pre>
+
+<h2 id="关于this的问题">关于"<code>this</code>"的问题</h2>
+
+<p>当你向 <code>setTimeout()</code> (或者其他函数)传递一个函数时,该函数中的<code>this</code>指向跟你的期望可能不同,这个问题在 <a href="/en-US/docs/JavaScript/Reference/Operators/this#Method_binding" title="en-US/docs/Core_JavaScript_1.5_Reference/Operators/Special_Operators/this_Operator#Method_binding">JavaScript reference</a> 中进行了详细解释.</p>
+
+<h3 id="解释">解释</h3>
+
+<p>由<code>setTimeout()</code>调用的代码运行在与所在函数完全分离的执行环境上。这会导致,这些代码中包含的 <code>this</code> 关键字在非严格模式会指向 <code>window</code> (或全局)对象,严格模式下为 undefined,这和所期望的<code>this</code>的值是不一样的。</p>
+
+<div class="note">
+<p>备注:即使是在严格模式下,<code>setTimeout()</code>的回调函数里面的<code>this</code>仍然默认指向<code>window</code>对象, 并不是<code>undefined</code>。</p>
+</div>
+
+<p>查看下面的例子:</p>
+
+<pre class="brush: js notranslate">let myArray = ["zero", "one", "two"];
+myArray.myMethod = function (sProperty) {
+ alert(arguments.length &gt; 0 ? this[sProperty] : this);
+};
+
+myArray.myMethod(); // prints "zero,one,two"
+myArray.myMethod(1); // prints "one"</pre>
+
+<p>上面这段代码正常工作,用<code>myArray</code>调用,在函数内,<code>this[sProperty]</code>等于 <code>myArray[sProperty]</code>。然后,下面这个例子:</p>
+
+<pre class="brush: js notranslate">setTimeout(myArray.myMethod, 1000); // prints "[object Window]" after 1 second
+setTimeout(myArray.myMethod, 1500, "1"); // prints "undefined" after 1.5 seconds</pre>
+
+<p><code>myArray.myMethod</code>函数传递给 <code>setTimeout</code>,到了定时时间,<code>this</code>没有指向,默认指向<code>window</code>对象。并且没有方法把 <code>thisArg</code> 传递给<code>setTimeout</code>,正如Array方法的<code>forEach</code>,<code>reduce</code>等。下面的例子表示使用<code>call</code>方法设置<code>this</code>也没用。</p>
+
+<pre class="brush: js notranslate">setTimeout.call(myArray, myArray.myMethod, 2000); // error: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Illegal operation on WrappedNative prototype object"
+setTimeout.call(myArray, myArray.myMethod, 2500, 2); // same error</pre>
+
+<h3 id="可能的解决方案">可能的解决方案</h3>
+
+<p>一个通用的方法是用包装函数来设置<code>this</code>:</p>
+
+<pre class="brush: js notranslate">setTimeout(function(){myArray.myMethod()}, 2000); // prints "zero,one,two" after 2 seconds
+setTimeout(function(){myArray.myMethod('1')}, 2500); // prints "one" after 2.5 seconds</pre>
+
+<p>箭头函数也可以:</p>
+
+<pre class="brush: js notranslate">setTimeout(() =&gt; {myArray.myMethod()}, 2000); // prints "zero,one,two" after 2 seconds
+setTimeout(() =&gt; {myArray.myMethod('1')}, 2500); // prints "one" after 2.5 seconds</pre>
+
+<p>另一个解决<code>this</code>问题的方法是使用两个非原生的 <code>setTimeout()</code> 和 <code>setInterval()</code> 全局函数代替原生的。该非原生的函数通过使用<a href="/en-US/docs/JavaScript/Reference/Global_Objects/Function/call" title="en-US/docs/JavaScript/Reference/Global_Objects/Function/call"><code>Function.prototype.call</code></a> 方法激活了正确的作用域。下面的代码显示了应该如何替换:</p>
+
+<pre class="brush: js notranslate">// Enable the passage of the 'this' object through the JavaScript timers
+
+var __nativeST__ = window.setTimeout, __nativeSI__ = window.setInterval;
+
+window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
+  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
+  return __nativeST__(vCallback instanceof Function ? function () {
+    vCallback.apply(oThis, aArgs);
+  } : vCallback, nDelay);
+};
+
+window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
+  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
+  return __nativeSI__(vCallback instanceof Function ? function () {
+    vCallback.apply(oThis, aArgs);
+  } : vCallback, nDelay);
+};</pre>
+
+<div class="note"><strong>备注:</strong> 这两个替换也让 IE支持了符合 HTML5 标准的定时器函数。所以也能作为一个 polyfills。查看 <a href="#Callback_arguments">Callback arguments</a> 一段.</div>
+
+<p>新特性检测:</p>
+
+<pre class="brush: js notranslate">let myArray = ["zero", "one", "two"];
+myArray.myMethod = function (sProperty) {
+ alert(arguments.length &gt; 0 ? this[sProperty] : this);
+};
+
+setTimeout(alert, 1500, "Hello world!"); // the standard use of setTimeout and setInterval is preserved, but...
+setTimeout.call(myArray, myArray.myMethod, 2000); // prints "zero,one,two" after 2 seconds
+setTimeout.call(myArray, myArray.myMethod, 2500, 2); // prints "two" after 2,5 seconds
+</pre>
+
+<p>针对这个问题并没有原生的解决方案。</p>
+
+<div class="note"><strong>注:</strong>JavaScript 1.8.5 引入了 <code><a href="/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind" title="en-US/docs/JavaScript/Reference/Global Objects/Function/bind">Function.prototype.bind()</a></code> 方法,该方法允许显式地指定函数调用时 this 所指向的值 。该方法可以帮助你解决 this 指向不确定的问题。</div>
+
+<p>使用<code>bind()</code>的例子:</p>
+
+<pre class="brush: js notranslate">let myArray = ['zero', 'one', 'two'];
+myBoundMethod = (function (sProperty) {
+ console.log(arguments.length &gt; 0 ? this[sProperty] : this);
+}).bind(myArray);
+
+myBoundMethod(); // prints "zero,one,two" because 'this' is bound to myArray in the function
+myBoundMethod(1); // prints "one"
+setTimeout(myBoundMethod, 1000); // still prints "zero,one,two" after 1 second because of the binding
+setTimeout(myBoundMethod, 1500, "1"); // prints "one" after 1.5 seconds
+</pre>
+
+<h2 id="备注">备注</h2>
+
+<p>你可以使用 {{domxref("WindowOrWorkerGlobalScope.clearTimeout()","clearTimeout()")}}来取消定时器。</p>
+
+<p>如果你希望你的代码被重复的调用 (比如每 N 毫秒一次),考虑使用{{domxref("WindowOrWorkerGlobalScope.setInterval()","setInterval()")}}。</p>
+
+<h3 id="传递字符串字面量">传递字符串字面量</h3>
+
+<p>向<code>setTimeout()</code>传递一个字符串而不是函数会遭受到与使用<code><a href="/en-US/docs/JavaScript/Reference/Global_Objects/eval#Don.27t_use_eval.21" title="https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/eval">eval</a></code>一样的风险.</p>
+
+<pre class="brush: js notranslate">// 推荐
+window.setTimeout(function() {
+ alert("Hello World!");
+}, 500);
+
+// 不推荐
+window.setTimeout("alert(\"Hello World!\");", 500);
+
+</pre>
+
+<p>字符串会在全局作用域内被解释执行,所以当<code>setTimeout()</code>函数执行完毕后,字符串中的变量不可用.</p>
+
+<h3 id="实际延时比设定值更久的原因:最小延迟时间">实际延时比设定值更久的原因:最小延迟时间</h3>
+
+<p>有很多因素会导致setTimeout的回调函数执行比设定的预期值更久,本节将讨论最常见的原因。</p>
+
+<h4 id="最小延时_>4ms">最小延时 &gt;=4ms</h4>
+
+<p>在浏览器中,<code>setTimeout()/</code>{{domxref("WindowOrworkerGlobalScope.setInterval","setInterval()")}} 的每调用一次定时器的最小间隔是4ms,这通常是由于函数嵌套导致(嵌套层级达到一定深度),或者是由于已经执行的setInterval的回调函数阻塞导致的。例如:</p>
+
+<pre class="brush: js notranslate" id="ct-0"><code>function cb() { f(); setTimeout(cb, 0); }
+setTimeout(cb, 0);</code></pre>
+
+<pre class="brush: js notranslate"><code>setInterval(f, 0);</code></pre>
+
+<p>在Chrome 和 Firefox中, 定时器的第5次调用被阻塞了;在Safari是在第6次;Edge是在第3次。Gecko 从这个版本 <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/56">version 56</a>开始对 <code>setInterval()</code> 开始采用这样的机制(<code>setTimeout()</code>已经实现,具体请参考以下内容)。</p>
+
+<p>一直以来,不同浏览器中出现这种最小延迟的情况有所不同(例如Firefox) - 从其他地方调用了setInterval( ),或者在嵌套函数调用setTimeout( ) 时(嵌套级别达到特定深度时),都会出现超时延迟。</p>
+
+<p>如果想在浏览器中实现0ms延时的定时器,你可以参考<a href="http://dbaron.org/log/20100309-faster-timeouts">这里</a>所说的{domxref("window.postMessage()")}}</p>
+
+<div class="note">
+<p><strong>Note</strong>: 最小延时, <code>DOM_MIN_TIMEOUT_VALUE</code>, 是4ms  (但在Firefox中通常是是存储在 <code>dom.min_timeout_value </code>这个变量中), <code>DOM_CLAMP_TIMEOUT_NESTING_LEVEL</code> 的第5层.</p>
+</div>
+
+<div class="note">
+<p><strong>Note</strong>: 4 ms 是在  <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#timers">HTML5 spec </a> 中精确的,并且在2010年及以后的跨浏览器中保持了一致,这个数值比 {{geckoRelease("5.0")}}规定的嵌套函数的最小延时10ms更为精确。</p>
+</div>
+
+<h4 id="未被激活的tabs的定时最小延迟>1000ms">未被激活的tabs的定时最小延迟&gt;=1000ms</h4>
+
+<p>为了优化后台tab的加载损耗(以及降低耗电量),在未被激活的tab中定时器的最小延时限制为1S(1000ms)。</p>
+
+<p>Firefox 从version 5 (see {{bug(633421)}}开始采取这种机制,1000ms的间隔值可以通过 <code>dom.min_background_timeout_value</code> 改变。Chrome 从 version 11 (<a href="http://crbug.com/66078">crbug.com/66078</a>)开始采用。</p>
+
+<p>Android 版的Firefox对未被激活的后台tabs的使用了15min的最小延迟间隔时间 ,并且这些tabs也能完全不被加载。</p>
+
+<div class="note">
+<p>当 Web Audio API {{domxref("AudioContext")}} 正在被用来播放音频的时候,Firefox 50不会再限制后台tabs的加载。 后续的Firefox 51 版本修正了这个问题,即使在没有音频播放的情况下,也不再限制后台tabs的加载。这个解决了一些软件应用在后台tabs中播放基于文本的音频( note-based) 时,无法去同步音频和画面的问题。</p>
+</div>
+
+<h4 id="追踪型脚本的最小延时限制">追踪型脚本的最小延时限制</h4>
+
+<p>从Firefox 55版本开始,追踪型脚本(例如 谷歌分析,或者其他的一些被Firefox 的 <a href="https://wiki.mozilla.org/Security/Tracking_protection#Lists">TP lists</a> 识别为追踪型脚本的 外链URL脚本)是重点限制加载的对象。在当前正在使用的页面中,这个节流限制的延时依然是4ms。但是在后台tabs中,这个最小延时限制是10000ms(10s),这个限制会在文档第一次加载后的30s后生效。</p>
+
+<p>控制这些行为的属性包含以下这些:</p>
+
+<ul>
+ <li><code>dom.min_tracking_timeout_value</code>: 4</li>
+ <li><code>dom.min_tracking_background_timeout_value</code>: 10000</li>
+ <li><code>dom.timeout.tracking_throttling_delay</code>: 30000</li>
+</ul>
+
+<h4 id="超时延迟">超时延迟</h4>
+
+<p>除了"最小延时"之外,定时器仍然有可能因为当前页面(或者操作系统/浏览器本身)被其他任务占用导致延时。 需要被强调是, 直到调用 <code>setTimeout()</code>的主线程执行完其他任务之后,回调函数和代码段才能被执行。例如:</p>
+
+<pre class="notranslate"><code>function foo() {
+ console.log('foo has been called');
+}
+setTimeout(foo, 0);
+console.log('After setTimeout');</code></pre>
+
+<p>会在控制台输出:</p>
+
+<pre class="notranslate"><code>After setTimeout
+foo has been called</code></pre>
+
+<p>出现这个结果的原因是,尽管<code>setTimeout</code> 以0ms的延迟来调用函数,但这个任务已经被放入了队列中并且等待下一次执行;并不是立即执行;队列中的等待函数被调用之前,当前代码必须全部运行完毕,因此这里运行结果并非预想的那样。</p>
+
+<h3 id="WebExtension_Background_pages和定时器">WebExtension Background pages和定时器</h3>
+
+<p>在 <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions">WebExtension</a> 中 background pages,timers工作不正常。这主要因为background pages实际上,并不是一次性全部加载:如果浏览器没有使用它,就不加载,如果需要就恢复。这对于WebExtension是透明的,但是有些事件(包括JS的timers)不会在不加载/恢复循环中执行,所以WebExtension中建议使用alarms。更详细的细节在<a href="https://developer.chrome.com/extensions/background_migration">合并到事件驱动后台脚本</a>。</p>
+
+<p>在本文写作的时候,只有Chrome展示了如上的特性 — Firefox没有未加载/恢复循环的行为,所以timers仍然可以工作。但是,仍然建议不要在WebExtension中使用timers:</p>
+
+<p>1.兼容Chorme。</p>
+
+<p>2.未来行为的改变会引起问题。</p>
+
+<h3 id="最大延时值">最大延时值</h3>
+
+<p>包括 IE,  Chrome, Safari, Firefox 在内的浏览器其内部以32位带符号整数存储延时。这就会导致如果一个延时(delay)大于 2147483647 毫秒 (大约24.8 天)时就会溢出,导致定时器将会被立即执行。</p>
+
+<h2 id="规范">规范</h2>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">Specification</th>
+ <th scope="col">Status</th>
+ <th scope="col">Comment</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>{{SpecName('HTML WHATWG', 'webappapis.html#dom-settimeout', 'WindowOrWorkerGlobalScope.setTimeout()')}}</td>
+ <td>{{Spec2("HTML WHATWG")}}</td>
+ <td>Method moved to the <code>WindowOrWorkerGlobalScope</code> mixin in the latest spec.</td>
+ </tr>
+ <tr>
+ <td>{{SpecName("HTML WHATWG", "webappapis.html#dom-settimeout", "WindowTimers.setTimeout()")}}</td>
+ <td>{{Spec2("HTML WHATWG")}}</td>
+ <td>Initial definition (DOM Level 0)</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="浏览器兼容性">浏览器兼容性</h2>
+
+<div id="compat-desktop">
+<div class="hidden">
+<p>The compatibility table on this page is generated from structured data. If you'd like to contribute to the data, please check out <a href="https://github.com/mdn/browser-compat-data">https://github.com/mdn/browser-compat-data</a> and send us a pull request.</p>
+</div>
+
+<p>{{Compat("api.WindowOrWorkerGlobalScope.setTimeout")}}</p>
+</div>
+
+<h2 id="See_also" name="See_also" style="margin-bottom: 20px; line-height: 30px; font-size: 2.14285714285714rem;">相关链接:</h2>
+
+<ul>
+ <li><a href="https://developer.mozilla.org/en-US/docs/JavaScript/Timers" title="/en-US/docs/JavaScript/Timers">JavaScript timers</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Timer.jsm" title="/en-US/docs/Mozilla/JavaScript_code_modules/Timer.jsm">Timer.jsm</a></li>
+ <li>{{domxref("WindowOrWorkerGlobalScope.clearTimeout")}}</li>
+ <li>{{domxref("WindowTimers.setInterval")}}</li>
+ <li>{{domxref("window.requestAnimationFrame")}}</li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/JavaScript/Timers/Daemons" title="/en-US/docs/JavaScript/Timers/Daemons"><em>Daemons</em> management</a></li>
+</ul>