Window: setTimeout() method

Baseline Widely available *

This feature is well established and works across many devices and browser versions. It’s been available across browsers since ⁨July 2015⁩.

* Some parts of this feature may have varying levels of support.

Warning: When the code parameter is used, this method dynamically executes its value as JavaScript. APIs like this are known as injection sinks, and are potentially a vector for cross-site-scripting (XSS) attacks.

You can mitigate this risk by always assigning TrustedScript objects instead of strings and enforcing trusted types. See Security considerations for more information.

The setTimeout() method of the Window interface sets a timer which executes a function or specified piece of code once the timer expires.

Syntax

js
setTimeout(code)
setTimeout(code, delay)

setTimeout(func)
setTimeout(func, delay)
setTimeout(func, delay, param1)
setTimeout(func, delay, param1, param2)
setTimeout(func, delay, param1, param2, /* …, */ paramN)

Parameters

func

A function to be executed after the timer expires.

code

A TrustedScript or a string of arbitrary code that is compiled and executed every delay milliseconds. This can be used instead of passing a function, but is strongly discouraged for the same reasons that make using eval() a security risk.

delay Optional

The time that the timer should wait before the specified function or code is executed, in milliseconds. Defaults to 0 if not specified.

Note:

param1, …, paramN Optional

Additional arguments which are passed through to the function specified by func.

Return value

A positive integer (typically within the range of 1 to 2,147,483,647) that uniquely identifies the timer created by the call. This identifier, often referred to as a "timeout ID", can be passed to clearTimeout() to cancel the timer.

Within the same global environment (e.g., a specific window or worker) the timeout ID is guaranteed not to be reused for any new timer as long as the original timer remains active. However, separate global environments maintain their own independent pools of timer IDs.

Exceptions

SyntaxError

The code can't be parsed as a script.

TypeError

Thrown if the code parameter is set to a string when Trusted Types are enforced by a CSP and no default policy is defined. It is also thrown if the first parameter is not one of the supported types: a function, string or TrustedScript.

Description

The setTimeout() function is commonly used to call a function that is executed just once, after a delay. The timeout can be cancelled before it completes using Window.clearTimeout().

If you wish to call a function repeatedly (e.g., every N milliseconds), you can use setInterval().

Working with asynchronous functions

setTimeout() is an asynchronous function, meaning that it completes immediately, and does not block the execution of the following code. In other words, you cannot use setTimeout() to create a "pause" before the next line is executed.

See the following example:

js
setTimeout(() => {
  console.log("this is the first message");
}, 5000);
setTimeout(() => {
  console.log("this is the second message");
}, 3000);
setTimeout(() => {
  console.log("this is the third message");
}, 1000);

// Output:

// this is the third message
// this is the second message
// this is the first message

Notice that the first function does not create a 5-second "pause" before calling the second function. Instead, the first function is called, but waits 5 seconds to execute. While the first function is waiting to execute, the second function is called, and a 3-second wait is applied to the second function before it executes. Since neither the first nor the second function's timers have completed, the third function is called and completes its execution first. Then the second follows. Then finally the first function is executed after its timer finally completes.

To create a progression in which one function only fires after the completion of another function, see the documentation on Promises.

Functions are called with the global this

Methods or functions passed to setTimeout() do not run in the same execution context as setTimeout(), and hence do not have the same this as the function that called setTimeout(). Instead the called function has a this keyword set to the window (or global) object. This problem is explained in detail in the JavaScript reference.

The following example demonstrates how this can cause unexpected behavior:

js
myArray = ["zero", "one", "two"];

myArray.myMethod = function (sProperty) {
  alert(arguments.length > 0 ? this[sProperty] : this);
};

myArray.myMethod(); // prints "zero,one,two"
myArray.myMethod(1); // prints "one"
setTimeout(myArray.myMethod, 1000); // Alerts "[object Window]" after 1 second
setTimeout(myArray.myMethod, 1500, "1"); // Alerts "undefined" after 1.5 seconds

You can use arrow functions to adopt the this of the function in which setTimeout() is called (arrow functions have a lexical this).

You can test this with the following code.

js
setTimeout(() => myArray.myMethod(), 1000); // Alert "zero,one,two" after 1 second
setTimeout(() => myArray.myMethod(1), 1500); // Alert "one" after 1.5 seconds
setTimeout(() => myArray.myMethod(2), 3000); // Alert "one" after 3 seconds

You might also use the Function.prototype.bind() method, which lets you specify the value that should be used as this for all calls to a given function. That lets you bypass problems where it's unclear what this will be, depending on the context from which your function was called:

js
const myArray = ["zero", "one", "two"];
const myBoundMethod = function (sProperty) {
  console.log(arguments.length > 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, 1.0 * 1000); // still prints "zero,one,two" after 1 second because of the binding
setTimeout(myBoundMethod, 1.5 * 1000, "1"); // prints "one" after 1.5 seconds

Non-number delay values are silently coerced into numbers

If setTimeout() is called with delay value that's not a number, implicit type coercion is silently done on the value to convert it to a number. For example, the following code incorrectly uses the string "1000" for the delay value, rather than the number 1000 – but it nevertheless works, because when the code runs, the string is coerced into the number 1000, and so the code executes 1 second later.

js
setTimeout(() => {
  console.log("Delayed for 1 second.");
}, "1000");

In many cases, the implicit type coercion can lead to unexpected and surprising results. For example, when the following code runs, the string "1 second" ultimately gets coerced into the number 0 — and so, the code executes with zero delay.

js
setTimeout(() => {
  console.log("Delayed for 1 second.");
}, "1 second");

Therefore, don't use strings for the delay value but instead always use numbers:

js
setTimeout(() => {
  console.log("Delayed for 1 second.");
}, 1000);

Maximum delay value

The delay argument is converted to a signed 32-bit integer, which limits the value to 2147483647 ms, or roughly 24.8 days. Delays of more than this value will cause an integer overflow. So for example, this code:

js
setTimeout(() => console.log("hi!"), 2 ** 32 - 5000);

…results in the timeout being executed immediately (since 2**32 - 5000 overflows to a negative number), while the following code:

js
setTimeout(() => console.log("hi!"), 2 ** 32 + 5000);

…results in the timeout being executed after approximately 5 seconds.

Note: In Node.js, any timeout larger than 2,147,483,647 ms results in immediate execution.

Reasons for longer delays than specified

There are a number of reasons why a timeout may take longer to fire than anticipated. This section describes the most common reasons.

Nested timeouts

As specified in the HTML standard, browsers will enforce a minimum timeout of 4 milliseconds once a nested call to setTimeout has been scheduled 5 times.

This can be seen in the following example, in which we nest a call to setTimeout with a delay of 0 milliseconds, and log the delay each time the handler is called. The first four times, the delay is approximately 0 milliseconds, and after that it is approximately 4 milliseconds:

html
<button id="run">Run</button>
<table>
  <thead>
    <tr>
      <th>Previous</th>
      <th>This</th>
      <th>Actual delay</th>
    </tr>
  </thead>
  <tbody id="log"></tbody>
</table>
js
let last = 0;
let iterations = 10;

function timeout() {
  // log the time of this call
  log(new Date().getMilliseconds());
  // if we are not finished, schedule the next call
  if (iterations-- > 0) {
    setTimeout(timeout, 0);
  }
}

function run() {
  // clear the log
  const log = document.querySelector("#log");
  while (log.lastElementChild) {
    log.removeChild(log.lastElementChild);
  }

  // initialize iteration count and the starting timestamp
  iterations = 10;
  last = new Date().getMilliseconds();
  // start timer
  setTimeout(timeout, 0);
}

function log(now) {
  // log the last timestamp, the new timestamp, and the difference
  const tableBody = document.getElementById("log");
  const logRow = tableBody.insertRow();
  logRow.insertCell().textContent = last;
  logRow.insertCell().textContent = now;
  logRow.insertCell().textContent = now - last;
  last = now;
}

document.querySelector("#run").addEventListener("click", run);

Timeouts in inactive tabs

To reduce the load (and associated battery usage) from background tabs, browsers will enforce a minimum timeout delay in inactive tabs. It may also be waived if a page is playing sound using a Web Audio API AudioContext.

The specifics of this are browser-dependent:

  • Firefox Desktop has a minimum timeout of 1 second for inactive tabs.

  • Firefox for Android has a minimum timeout of 15 minutes for inactive tabs and may unload them entirely.

  • Firefox does not throttle inactive tabs if the tab contains an AudioContext.

  • Chrome uses different levels of throttling depending on the tab activity:

    • Minimal throttling: Applies to timers when the page is visible, has made sound recently, or is otherwise considered active by Chrome. Timers run close to the requested interval.

    • Throttling: Applies to timers when minimal throttle conditions are not met and any of these conditions are true:

      • Nesting count (i.e., number of chained timer calls) is lower than 5.
      • Page has been invisible for less than 5 minutes.
      • WebRTC is active.

    Timers in this state are checked once per second, which may be batched together with other timers that have similar timeouts.

    • Intensive throttling: Introduced in Chrome 88 (January 2021). Applies to timers when neither minimal throttling nor throttling conditions are met, and all of the following conditions are met:
      • Nesting count is 5 or higher.
      • Page has been invisible for more than 5 minutes.
      • Page has been silent for more than 30 seconds.
      • WebRTC is inactive.

    Timers in this state are checked once per minute, which may be batched together with other timers that have similar timeouts.

Throttling of tracking scripts

Firefox enforces additional throttling for scripts that it recognizes as tracking scripts. When running in the foreground, the throttling minimum delay is still 4ms. In background tabs, however, the throttling minimum delay is 10,000 ms, or 10 seconds, which comes into effect 30 seconds after a document has first loaded.

See Tracking Protection for more details.

Late timeouts

The timeout can also fire later than expected if the page (or the OS/browser) is busy with other tasks. One important case to note is that the function or code snippet cannot be executed until the thread that called setTimeout() has terminated. For example:

js
function foo() {
  console.log("foo has been called");
}
setTimeout(foo, 0);
console.log("After setTimeout");

Will write to the console:

After setTimeout
foo has been called

This is because even though setTimeout was called with a delay of zero, it's placed on a queue and scheduled to run at the next opportunity; not immediately. Currently-executing code must complete before functions on the queue are executed, thus the resulting execution order may not be as expected.

Deferral of timeouts during pageload

Firefox will defer firing setTimeout() timers while the current tab is loading. Firing is deferred until the main thread is deemed idle (similar to Window.requestIdleCallback()), or until the load event is fired.

WebExtension background pages and timers

In WebExtensions, setTimeout() does not work reliably. Extension authors should use the alarms API instead.

Security considerations

The method can be used to execute arbitrary input passed in the code parameter. If the input is a potentially unsafe string provided by a user, this is a possible vector for Cross-site-scripting (XSS) attacks. For example, the following example assumes the scriptElement is an executable <script> element, and that untrustedCode was provided by a user:

js
const untrustedCode = "alert('Potentially evil code!');";
const id = setTimeout(untrustedCode, 1000);

Websites with a Content Security Policy (CSP) will prevent such code running by default; if you need to use the method with code then you will first need to allow the unsafe-eval in your CSP script-src.

If you must allow the scripts to run you can mitigate these issues by always assigning TrustedScript objects instead of strings, and enforcing trusted types using the require-trusted-types-for CSP directive. This ensures that the input is passed through a transformation function.

The behavior of the transformation function will depend on the specific use case that requires a user provided script. If possible you should lock the allowed scripts to exactly the code that you trust to run. If that is not possible, you might allow or block the use of certain functions within the provided string.

Examples

Setting and clearing timeouts

The following example sets up two simple buttons in a web page and hooks them to the setTimeout() and clearTimeout() routines. Pressing the first button will set a timeout which shows a message after two seconds and stores the timeout id for use by clearTimeout(). You may optionally cancel this timeout by pressing on the second button.

HTML

html
<button id="show">Show a message after two seconds</button>
<button id="cancel">Cancel message before it happens</button>

<div id="output"></div>

JavaScript

js
let timeoutID;

function setOutput(outputContent) {
  document.querySelector("#output").textContent = outputContent;
}

function delayedMessage() {
  setOutput("");
  timeoutID = setTimeout(setOutput, 2 * 1000, "That was really slow!");
}

function clearMessage() {
  clearTimeout(timeoutID);
}

document.getElementById("show").addEventListener("click", delayedMessage);
document.getElementById("cancel").addEventListener("click", clearMessage);

Result

See also the clearTimeout() example.

Specifications

Specification
HTML
# dom-settimeout-dev

Browser compatibility

See also