A single thread of JavaScript

JavaScript is a single-threaded language, and browsers only allocate one thread to JS to execute synchronized code. But JavaScript is designed to be single-threaded for the following reasons.

Assuming that two JS threads operate DOM or compete for unique resources at the same time, browsers need to introduce the concept of locking to determine the validity of these two JS operations. Therefore, in order to avoid the complexity of introducing locks, JS is designed as a single thread.

In addition, JS was originally designed as a language for I/O, so single threading is also to avoid wasting resources. So in modern browsers, the HTML5 standard introduces Worker syntax for multithreaded execution (workers don’t have access to the DOM).

synchronous

Console. log(' sync one '); Console. log(' sync two '); // Result synchronization one synchronization twoCopy the code

Synchronization is the execution of code step by step, with each line of code executed before the next step.

asynchronous

console.log('log1'); setTimeout(()=>console.log('log2'),0); console.log('log3'); // result log1 log3 log2Copy the code

Why asynchronous? This is because using synchronous code for time-consuming tasks (ajax, event listeners, timers, etc.) will cause the browser to fake death. So browsers create separate threads for asynchronous events to execute.

The browser adds asynchronous tasks to a Task Queue. After the synchronization Task is complete, the browser enters the Task Queue to execute the completed Task. And event-loop in the task queue

In the figure above, when the main thread is running, the heap and stack are created, and the code in the stack calls various external apis, which add various events (click, load, done) to the “task queue”. As soon as the stack completes, the main thread reads the “task queue” and executes the corresponding callback function for those events.

Why asynchronous?

If the execution of a time-consuming task, simultaneous execution of logic would lock behind all of the code, and with asynchronous, the browser will asynchronous code out of the synchronous thread of execution, in asynchronous execution, first of all, avoid browser feign death, the second is to use the asynchronous thread to speed up the whole code execution.

(Photo by Soham Kaman)

Asynchronous processes

(1) All synchronization tasks are executed on the main thread, forming an execution context stack.

(2) In addition to the main thread, there is a task queue. Whenever an asynchronous task has a result, an event is placed in the “task queue”.

(3) Once all synchronization tasks in the “execution stack” are completed, the system reads the “task queue” to see what events are in it. Those corresponding asynchronous tasks then end the wait state, enter the execution stack, and start executing.

(4) The main thread repeats step 3 above.

Event-Loop

Event Loop

The main thread reads events from the “task queue” in a continuous Loop, so the whole operation mechanism is also called an Event Loop.

Differences between Macrotasks and microtasks

Macrotasks: setTimeout setInterval setImmediate I/O UI rendering

microtasks: Promise process.nextTick Object.observe MutationObserver

After executing the synchronized code, the main thread reads the Event of the task queue. If a MacroTask is generated in the event-loop, the macroTask will be put into the next execution, while the microTask will be completed directly in this loop. Whether or not you join multiple Microtasks.

In simple terms, macroTask is executed next time, microTask is executed this time, and new microTasks are executed last.

Task queue (message queue)

A “task queue” is a queue of events (also known as a message queue). When a worker thread completes a task, it adds an event (also known as sending a message) to the “task queue”, indicating that the related asynchronous task can be placed on the “execution stack”. The main thread reads the “task queue”, which is to read what events are in it.

Which threads the browser owns

  1. JavaScript engine threads
  2. Interface (GUI) rendering thread
  3. The browser event triggers the thread
  4. Http request thread
  5. Timed trigger thread

JavaScript engine threads

Javascript engines, also known as JS cores, are responsible for handling Javascript scripts, such as the V8 engine. Javascript engine threads are, of course, responsible for parsing Javascript scripts and running code.

Interface rendering thread

The GUI rendering thread is responsible for rendering the HTML elements of the browser interface, which is executed when the interface needs to be repainted or reflow due to some operation. While the Javascript engine runs the script, the GUI rendering thread is suspended, that is, “frozen.”

GUI rendering threads are mutually exclusive with JavaScript engine threads!

Because JavaScript is DOM manipulable, if you render the interface while modifying these element attributes (that is, the JavaScript thread and the UI thread are running at the same time), you might get inconsistent element data before and after the render thread. Therefore, to prevent unexpected results from rendering, the browser sets the GUI rendering thread to be mutually exclusive with the JavaScript engine. The GUI thread is suspended while the JavaScript engine is executing, and GUI updates are stored in a queue until the engine thread is idle.

Timing trigger thread

The browser timing counter is not counted by the JavaScript engine, which is single-threaded and can affect timing accuracy if it is blocked. Therefore, it is more reasonable to use a separate thread to time and trigger timing.

Event trigger thread

When an event is triggered, the thread adds the event to the end of the queue, waiting for the JS engine to process it. These events can be currently executing code blocks such as scheduled tasks, or other threads from the browser kernel such as mouse clicks, AJAX asynchronous requests, etc., but due to the single-threaded nature of JS all these events have to queue up for the JS engine to process.

Asynchronous HTTP request threads

If a callback function is set, the asynchronous thread generates the state change event and places it in the JavaScript engine’s processing queue to wait for processing when the XMLHttpRequest is detected after the connection by opening a new thread through the browser.

Cheat knowledge

The minimum setTimeout value set in HTML5 standard is 4ms, but the test found that the minimum value in WebKit is 1ms. Here is the test code

setTimeout(()=>console.log('log1'),1);
setTimeout(()=>console.log('log2'),0);
Copy the code

We want to print log of 2,log of 1. But it’s actually log1,log2. This is due to the minimum setTimeout in WebKit being 1ms. This is just one person’s idea. If you have a better idea, please let me know. The TKS git.webkit.org/?p=WebKit.g… The source code is set to 4_ms, now I do not know why. It also has Settings in the code, take the minimum.

static Seconds defaultMinimumInterval() { return 4_ms; }
Copy the code

There are two kinds of Chrome period, one kind is 4 ms, is a 1 ms chromium.googlesource.com/chromium/sr…

static const int kMaxIntervalForUserGestureForwarding =
    1000;  // One second matches Gecko.
static const int kMaxTimerNestingLevel = 5;
static const double kOneMillisecond = 0.001;
// Chromium uses a minimum timer interval of 4ms. We'd like to go
// lower; however, there are poorly coded websites out there which do
// create CPU-spinning loops.  Using 4ms prevents the CPU from
// spinning too busily and provides a balance between CPU spinning and
// the smallest possible interval timer.
static const double kMinimumInterval = 0.004;
  double interval_milliseconds =
      std::max(kOneMillisecond, interval * kOneMillisecond);
  if (interval_milliseconds < kMinimumInterval &&
      nesting_level_ >= kMaxTimerNestingLevel)
    interval_milliseconds = kMinimumInterval;
  if (single_shot)
    StartOneShot(interval_milliseconds, BLINK_FROM_HERE);
  else
    StartRepeating(interval_milliseconds, BLINK_FROM_HERE);
Copy the code