Table of Contents
Table of Contents
And if you think you understand it by now, “sure, a number adds a string returns a string…”, then try to explain this:
If type coercion is sometimes considered a beginner’s confusion, closure is more of an intermediate one, where it does not feel difficult at first glance but when you delve into the problems, it can be very tricky to master.
Consider the following snippets of code:
The reason for this behavior is a feature called closure. A closure is the combination of a function and the lexical environment within which that function was declared, including the variables of the outer-nested functions. The closure created in this code example is maintained by the testFunc reference to the inner() function instance, which retains its lexical environment, including the “name” variable. It seems now more confusing doesn’t it, and if you want a more hardcore example of closures, consider this code:
Fun fact: When I was writing this article, I asked a colleague of mine if he was familiar with the concept of closures. He asked me in return if by closures I meant “the conclusion of an article”. Yo what the f—!
The event queue can also be further divided into two branches: macrotasks and microtasks. Macrotasks are queued in the task queue and executed one at a time, in a first-in-first-out (FIFO) order. Common macrotasks include I/O events, timers, and rendering. On the other hand, microtasks are typically used for tasks that need to happen after the current task completes, but before the user can interact with the page. Examples of microtasks include promise callbacks, mutation observer callbacks, and queueMicrotask function calls. Because of this, the event loop, when monitoring pending events in the queue, first looks into and resolves the unfinished microtasks, before doing the same to the macrotasks.
Now, let’s get back to the question posed at the beginning of this section and analyze it. On the first run, the event loop checks the main call stack, which contains the two logging methods. Although the setTimeout is default to 0ms and the promise is resolved immediately, they do not belong to the call stack, hence they will not be checked on the first run. Afterwards, the event loop checks the event queue again, beginning with the microtasks, which contains the promise callback, and executes it. Finally, it loops again and checks the macrotasks, resolving the setTimeout. Therefore, the logged order will be: