How does setTimeout invoke a callback function in a browser?
Tags: javascript, html, webdev, beginners
Published: 2020-05-03 11:36:54

Assertion: *JavaScript is executed in a browser (but not in a web worker).*

Everyone in JavaScript world knows the **window.setTimeout** method, but let’s do a quick refresh.

One of the valid variants for this method has the following syntax (further in the text I will omit object `window.` part and leave only **setTimeout**):

```javascript
setTimeout(function[, delay, param1, param2, …])
```

The method contains one required parameter which should be a function also known as callback.

Also, there are several optional parameters (inside `[]` bracket in above code snippet). First optional parameter is a delay in ms after which time callback will be invoked. Second and subsequent optional parameters will be passed as arguments in the callback function.

Example:

```javascript
const add = function(a,b){
  console.log(a+b)
};

setTimeout(add,1000,1,2);

// 3 (in a second)
```

Simple! But what about infamous **this** keyword?

As we know **this** inside non-arrow functions is defined dynamically (**this** depends on how we call a function). But in the example above we do not invoke callback function ourselves. Here I mean that we do not type `add()` (function name `add` with parenthesis `()`). Instead we pass the function `add` as an argument to **setTimeout** and then **setTimeout** calls it. Actually from our code we do not know how **setTimeout** invokes the callback because we do not create **setTimeout**. It is predefined by a platform (in our case it is a browser).

Let’s first have a look at another example:

```javascript
const add = function(a,b){
  console.log(a+b,this)
};

const hoc = function(f,a,b){
  f(a,b)
};

hoc(add,1,2);

// 3,window (in non-strict mode)
// 3,undefined (in strict mode)
```

In this example, function `add` is a callback function which is passed as an argument to `hoc` function. But now we create function `hoc` and write invocation of callback inside `hoc` ourselves (we type parenthesis `()`). So everything works as we expect. Function `add` is called as a ‘normal’ function and **this** is defined as either **window in non-strict mode** or as **undefined in strict mode**.

Let’s go back to **setTimeout**. May we say that **setTimeout** invokes a callback function and set **this** in the same manner as we have just seen? “Yes” will be a wrong answer.

It feels like a perfect time to have a look inside a specification 📕

**setTimeout** method is NOT part of [JS spec (ECMA-262)](http://www.ecma-international.org/publications/standards/Ecma-262.htm) but a part of [HTML5 spec](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers) and it turns out that the method has it’s own rule to invoke a passed callback.

The rule looks like that:

>Invoke the Function. Use the third and subsequent method arguments (if any) as the arguments for invoking the Function. Use *method context proxy* as the callback **this** value.

Sounds professional, but what is *method context proxy*? No worries, in a browser (but not on a worker) *method context proxy* is just the **window** object.

Thus after a delay a setTimeout callback is invoked with explicitly given **this** value. It is a acceptable to think that the callback is invoked like that:

```javascript
function setTimeout(callback, delay, param3, param4, ...){
  // timer is count up passed delay and then
  callback.call(window, param3, param4, ...)
}
```

It may be concluded that **setTimeout** does not consider the mode (strict or non-strict) our code is run in but sets **this as window** during callback invocation.

Example (super strict mode):

```javascript
'use strict'

const add = function(a,b){ 
  'use strict'
  console.log(a+b, this)
};

setTimeout(add,1000,1,2);

// 3, window (in a second)
```

------
### Instead of conclusion:
* **setTimeout** is not part of JavaScript spec and is defined by a platform;
* **setTimeout** does not consider the mode type (strict or non-strict). It invokes non-arrow function callback and sets **this** to the **window** object in a browser (but not in a web worker);
* **setInterval** has the same rule for this in callback;
* In case a callback is an arrow function or a [bound function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind), **this** is defined as expected — in a static way;
* Keep calm and read specs :) 📕📗📘