Search code examples
javascripttask-queueevent-loop

Why is setTimeout acting synchronously?


Example of setTimeout being executed after a synchronous code is below it

console.log('hello');
setTimeout(() => console.log('timeout'), 0);
console.log('end');

Console Output: hello end timeout

Async:

function asyncForEach(array, callBack) {
    array.forEach(x => setTimeout(callBack(x), 0));
}
asyncForEach([1,2,3,4], (i) => console.log(i)); 
console.log('end');

Console Output:

1
2
3
4
end

Synchronous:

[1,2,3,4].forEach(x => console.log(x));
console.log('end');

Console output:

1
2
3
4
end

I am trying to better understand the event loop and the task queue, and after watching a well recommended video (https://www.youtube.com/watch?v=8aGhZQkoFbQ) came across the semi statement of: "Callbacks can be synchronous and asynchronous". The speaker went on to demonstrate how the synchronous way would not end up going through the task queue, and therefore the event loop, so everything remained on the stack. However, the asynchronous way would cause the task queue to be populated with the eventual return of the setTimeout callback, which would make any synchronous below code executable since the event loop has to wait for an empty stack to insert the returned callback.

When running the above code (something I stole from him and also edited, so it could be a wrong addition on my part), the asynchronous way produces the same results as the synchronous way.

Could someone help explain why the asynchronous way does not act as the very first setTimeout example and/or provide explanation of how regular callbacks (such as the array helper methods) are not inserted into the task queue therefore never messed with by the event loop?


Solution

  • Your "asynchronous way" isn't asynchronous, because you called the function and passed its return value to setTimeout:

    function asyncForEach(array, callBack) {
        array.forEach(x => setTimeout(callBack(x), 0)); // <-- callBack(x) calls callBack immediately
    }
    asyncForEach([1,2,3,4], (i) => console.log(i)); 
    console.log('end');
    

    If you want it delayed, make a function to pass to setTimeout that it can call later:

    function asyncForEach(array, callBack) {
        array.forEach(x => setTimeout(() => callBack(x), 0)); // <-- Makes closure that calls callBack(x) later
    }
    asyncForEach([1,2,3,4], (i) => console.log(i)); 
    console.log('end');