setTimeout没有延迟和立即执行函数一样吗?

时间:2010-08-26 22:29:55

标签: javascript delay settimeout timing

我正在查看Web应用程序中的一些现有代码。我看到了这个:

window.setTimeout(function () { ... })

这是否只是立即执行功能内容?

4 个答案:

答案 0 :(得分:82)

它不一定会立即运行,也不会将延迟显式设置为0.原因是setTimeout从执行队列中删除了该函数,并且只有在JavaScript完成当前执行队列后才会调用它。

console.log(1);
setTimeout(function() {console.log(2)});
console.log(3);
console.log(4);
console.log(5);
//console logs 1,3,4,5,2

有关详细信息,请参阅http://javascriptweblog.wordpress.com/2010/06/28/understanding-javascript-timers/

答案 1 :(得分:20)

setTimeout使用的最小延迟(根据HTML5为4毫秒,Firefox 3.6使用10毫秒)。 Mozilla开发人员中心documentation page对此进行了讨论。

答案 2 :(得分:1)

您缺少毫秒参数...

setTimeout(function() { /*something*/ }, 0);

0将延迟设置为0,但它实际上做的是让你的函数“跳转队列”浏览器执行列表。浏览器有很多事情要做,比如在页面上渲染对象,通过调用它,只要浏览器有一些循环,你的函数就会运行。

答案 3 :(得分:0)

简短的回答是不,它不一样。

MDN setTimeout documentationdelay 参数的说明是:

<块引用>

定时器在开始之前应该等待的时间,以毫秒为单位 执行指定的函数或代码。如果省略该参数, 使用值 0,表示“立即”执行,或更多 准确地说,下一个事件周期。请注意,在任何一种情况下,实际 延迟可能比预期的要长;请参阅延迟时间超过的原因 指定如下。

省略 delay 或使用 0 的值将在下一个事件周期中执行,但可能需要更长的时间。这是它与立即执行函数内容不同的第一个原因。例如:

document.getElementById("runNow").addEventListener("click", runNow);
document.getElementById("runNoDelay").addEventListener("click", runNoDelay);

function runNow() {
  clearLog();
  addLog("Start");
  addLog("Hello");
  addLog("End");
}

function runNoDelay() {
  clearLog();
  addLog("Start");
  setTimeout(function() {
    addLog("Hello");
  });
  addLog("End");
}

function clearLog() {
  const log = document.getElementById("log");
  while (log.lastElementChild) {
    log.removeChild(log.lastElementChild);
  }
}

function addLog(message) {
  const newLine = document.createElement("pre");
  newLine.textContent = message;
  document.getElementById("log").appendChild(newLine);
}
<button id="runNow">Run Now</button>
<button id="runNoDelay">Run With No Delay</button>
<div id="log"></div>

请注意,在无延迟地使用 setTimeout 时,消息“End”出现在“Hello”之前。

即使延迟被省略或为 0,也有可能导致延迟更长的原因,其中可能包括:

  • “...一旦对 setTimeout 的嵌套调用被安排了 5 次,浏览器将强制执行最少 4 毫秒的超时。”。
  • 浏览器标签处于非活动状态。
  • 该脚本已被识别为跟踪脚本并受到限制。
  • 队列中的其他任务花费的时间比执行延迟要长。
  • 无论出于何种原因,浏览器都决定使用其他一些实现定义的时间长度。

例如:

document.getElementById("run").addEventListener("click", run);

function run() {
  clearLog();
  const now = new Date().getMilliseconds();
  setTimeout(function() {
    timeout(0, now);
  });
}

function clearLog() {
  const log = document.getElementById("log");
  while (log.lastElementChild) {
    log.removeChild(log.lastElementChild);
  }
}

function timeout(nestingLevel, last) {
  const now = new Date().getMilliseconds();
  logline(nestingLevel, now, last);
  if (nestingLevel < 9) {
    setTimeout(function() {
      timeout(nestingLevel + 1, now);
    });
  }
}

function logline(nestingLevel, now, last) {
  const newLine = document.createElement("pre");
  newLine.textContent = `${nestingLevel}                ${now - last}`;
  document.getElementById("log").appendChild(newLine);
}
<button id="run">Run</button>
<pre>nesting level    delay</pre>
<div id="log"></div>

(改编自 MDN 上的示例)。

注意延迟是 0(或接近 0),直到嵌套级别达到某个点。

同样重要的是要注意 setTimeout 是使用包装函数 (function () { ... }) 调用的,这意味着 this 的值将与函数的内容相同立即被处决。


当嵌套级别为 4 时,Chrome(版本 92.0.4515.131)和 Firefox(版本 91.0)强制最小超时为 4 毫秒,如上例所示。

HTML Standard 表示“如果嵌套级别大于 5,并且超时小于 4,则将超时设置为 4”。

我的解释是这些浏览器过早地强制执行这个最小超时 2 个任务。应该是应用于嵌套级别为6(大于5)的第7个任务的调度。

在一天结束时,浏览器可以根据需要使用其他一些实现定义的时间长度,因此这是一个有趣但没有实际意义的问题。