attheoaks.com

Understanding JavaScript's Execution Mechanism: A Comprehensive Guide

Written on

Chapter 1: Introduction to JavaScript

In our everyday coding experiences, we often face scenarios where we need to predict the output and order of execution of a few lines of JavaScript code. Given that JavaScript operates on a single-threaded model, it executes commands sequentially based on their arrangement. For instance:

let a = '1';

console.log(a);

let b = '2';

console.log(b);

However, JavaScript behaves differently in scenarios involving asynchronous operations, such as:

setTimeout(function(){

console.log('start');

});

new Promise(function(resolve){

console.log('2');

for(var i = 0; i < 10000; i++){

i == 99 && resolve();

}

}).then(function(){

console.log('3');

});

console.log('end');

To truly grasp JavaScript's execution flow, we must delve deeper.

Section 1.1: Understanding JavaScript

JavaScript is inherently single-threaded, meaning that all its versions of “multithreading” are simulated within a single thread. There is a notable distinction between execution and operation. JavaScript can run in various environments, including Node.js, browsers, and Ringo, which handle execution differently. However, the JavaScript parsing engine operates uniformly across these platforms.

Subsection 1.1.1: The JavaScript Event Loop

JavaScript execution flow

JavaScript's single-threaded nature resembles a bank with one service window, where customers must wait in line for their turn. Similarly, tasks in JavaScript are executed sequentially. If one task is time-consuming, subsequent tasks must wait. This raises the question: if we want to view news articles with high-resolution images that load slowly, should our webpage freeze until they are fully rendered? Programmers cleverly categorize tasks into two types:

  • Synchronous Tasks: These are executed in the order they are received, such as rendering page elements.
  • Asynchronous Tasks: These are reserved for tasks that require more time, like loading images and audio files.

The JavaScript engine continuously monitors the main thread's execution stack. Once it is empty, it checks the Event Queue for any functions waiting to be executed.

let data = [];

$.ajax({

url: 'www.javascript.com',

data: data,

success: () => {

console.log('send');

}

});

console.log('end');

In this example, the Ajax call registers a callback function that enters the Event Table. The main thread proceeds to execute console.log('Code execution ends'); and, after the Ajax operation completes, the success callback is invoked.

This overview should provide you with a foundational understanding of JavaScript's execution order.

Section 1.2: setTimeout Explained

At first glance, setTimeout seems to allow for delayed asynchronous execution. For example:

setTimeout(() => {

console.log('3s');

}, 3000);

However, complications can arise. Sometimes, despite specifying a 3-second delay, the function might execute after 5 or 6 seconds. Consider this:

setTimeout(() => {

task();

}, 3000);

console.log('console');

Here, the synchronous task (console.log) executes before the asynchronous task.

To clarify the behavior of setTimeout, let’s explore a more complex example:

setTimeout(() => {

task();

}, 3000);

sleep(10000000);

In this scenario, task() is added to the Event Table with a 3-second delay, while the sleep function takes much longer to execute. As a result, although the timer completes, the task waits until the sleep function is finished.

Additionally, you may come across setTimeout(fn, 0). This does not execute immediately; rather, it schedules the task to run at the earliest opportunity after all synchronous tasks complete.

// code 1

console.log('start');

setTimeout(() => {

console.log('1');

}, 0);

// Output: start, 1

// code 2

console.log('start');

setTimeout(() => {

console.log('1');

}, 3000);

// Output: start, ...3s..., 1

It’s important to note that even when the main thread is free, a 0-millisecond delay cannot be reached instantly.

Section 1.3: setInterval Overview

setInterval allows for periodic execution, adding the registered function to the Event Queue at specified intervals. However, if a previous task takes too long, the next execution must also wait.

For setInterval(fn, ms), it’s crucial to understand that fn will not necessarily execute every ms seconds; instead, it will be queued every ms seconds. If the execution time of fn exceeds the interval, there will be no delay between executions.

Section 1.4: Promises and process.nextTick

process.nextTick(callback) serves a similar purpose to setTimeout, scheduling the callback function to run in the next cycle of the event loop.

We can classify tasks into:

  • Macro-tasks: Including the overall script, setTimeout, and setInterval.
  • Micro-tasks: Including Promise and process.nextTick.

Different task types enter their respective Event Queues. The event loop's order dictates the execution sequence of JavaScript code.

setTimeout(function() {

console.log('setTimeout');

});

new Promise(function(resolve) {

console.log('promise');

}).then(function() {

console.log('then');

});

console.log('console');

The sequence of execution involves entering the main thread as a macro-task, encountering setTimeout, then handling the Promise, and finally executing console.log().

To illustrate further, let’s analyze a more intricate code snippet:

console.log('1');

setTimeout(function() {

console.log('2');

process.nextTick(function() {

console.log('3');

});

new Promise(function(resolve) {

console.log('4');

resolve();

}).then(function() {

console.log('5');

});

});

process.nextTick(function() {

console.log('6');

});

new Promise(function(resolve) {

console.log('7');

resolve();

}).then(function() {

console.log('8');

});

setTimeout(function() {

console.log('9');

process.nextTick(function() {

console.log('10');

});

new Promise(function(resolve) {

console.log('11');

resolve();

}).then(function() {

console.log('12');

});

});

As we work through the event loop, we observe the output sequence and how the various tasks interact.

JavaScript event loop analysis

In conclusion, understanding JavaScript's execution mechanism is crucial for effective development. Thank you for reading, and I look forward to your engagement with more insightful content.

Explore the execution contexts in JavaScript through the video "JavaScript Visualized - Execution Contexts."

Discover the ultimate guide to JavaScript execution contexts with the video "The ULTIMATE guide to JavaScript Execution Contexts."

Share the page:

Twitter Facebook Reddit LinkIn

-----------------------

Recent Post:

Understanding Programming: Myths, Realities, and Skills

Discover essential insights about programming, debunk myths, and understand the skills needed to thrive in the digital age.

Setting Healthy Boundaries: A Guide to Self-Respect and Empowerment

Discover the importance of setting personal boundaries to foster respect and improve your relationships.

Advancements in Quantum Thermometry: A New Era of Precision

Researchers at Chalmers University have unveiled a groundbreaking thermometer that enhances precision in quantum computing.