I’ve been doing javascript development for years, on and off. There are still a few things I don’t understand, such as javascript asynchrony mentioned in this article. From setTimeout, setInterval, callback to Promise, Async, Node’s setImmediate, Process.nexttick and so on. I just know how to use it, but I still can’t understand how to handle asynchronous processing when a single thread is running. After seeing The teacher Pu Ling’s simple node, I just have a little feeling.

As a result, at the end of the year idle nothing more than looking for relevant information. Of course, predecessors planted trees, descendants enjoy the shade. All you have to do is collect, organize, and make yourself clear…

The following are personal understanding, say wrong do not like spray. JJ is short, long and ugly.

Let’s start with a question

setTimeout(() = >{
    console.log('timer1')

    Promise.resolve().then(function() {
        console.log('promise1')})},0)

setTimeout(() = >{
    console.log('timer2')

    Promise.resolve().then(function() {
        console.log('promise2')})},0)
Copy the code

Is the result the same in browser and Node? Browser results

timer1
promise1
timer2
promise2
Copy the code

Node environment results

timer1
timer2
promise1
promise2
Copy the code

Let’s think about single-threaded is when the thread in the JS engine that is responsible for parsing and executing the JS code has only one main thread, which only does one thing at a time, whereas we know that for an Ajax request, the main thread will do other things while waiting for a response.

Event Loop

People who create code are people, and most of their inspiration comes from life. For example, javascript processing is asynchronous, just like going to a restaurant. The waiter will place an order for the customer first, then give the customer’s order to the kitchen for preparation, and then go to serve the next customer, instead of waiting for the food to come out. Javascript breaks down the behavior of a customer’s order. There are no more than two types of drinks and non-drinks. This corresponds to macroTask and microTask in javascript. The specific division is as follows:

Macrotasks: script(overall code),setTimeout.setInterval,setImmediate,I/O,UI render
 microtasks: process.nextTick, Promises, ObjectMutationObserver. Observe (waste)Copy the code

But the steps are different in different scenarios, just like western food and Chinese food. Western food is divided into very detailed: first dish -> soup -> side dish -> main course -> vegetable dishes -> dessert -> coffee, Chinese food is relatively simple: cold dish -> hot dish -> soup. The implementation is different in different scenarios, the DIFFERENCES between the HTML standard and the NODE standard, as in the first example.

The browser

To better illustrate how the browser handles event loops look at the code

console.log('start')

setTimeout(function() {
  console.log('setTimeout')},0)

Promise.resolve().then(function() {
  console.log('promise1')
}).then(function() {
  console.log('promise2')})console.log('end')
Copy the code

How does he work? All in one:

Combined with the web_process picture we posted before, it suddenly becomes clear.

Tip: When to render a view

A single MacroTasks + microTasks is called a single ticket, and when the ticket ends, the browser redraws (not always)

In other words, the time it takes to perform the task affects the timing of the view rendering. Typically, the browser refreshes the page at 60 frames per second, which is said to be the best frame rate for human interaction, about 16.7ms per frame, so if you want the user to feel smooth, A single MacroTask and all its associated microtasks should be completed preferably within 16.7ms.

However, not every round of the event loop will perform a view update. Browsers have their own optimization strategies, such as adding several view updates together to redraw, and notifying requestAnimationFrame to perform a callback before redrawing. That is, the requestAnimationFrame callback is executed during the UI render phase of one or more event loops

Try this for yourself:

setTimeout(function() {console.log('timer1')}, 0)

requestAnimationFrame(function(){
    console.log('requestAnimationFrame')})setTimeout(function() {console.log('timer2')}, 0)

new Promise(function executor(resolve) {
    console.log('promise 1')
    resolve()
    console.log('promise 2')
}).then(function() {
    console.log('promise then')})console.log('end')
Copy the code

A quick summary:

  1. Event loops are the core of JS asynchrony

  2. Each event cycle is divided into three steps:

  • A) Execute a task in the MacroTask queue
  • B) Execute all tasks in the current MicroTask queue
  • c) UI render
  1. The browser only guarantees that the requestAnimationFrame will be redrawn before it is redrawn. There is no fixed time, and it is up to the browser to decide when to redraw it!

The Node environment

Let’s go back to the beginning of Demo1

setTimeout(() = >{
    console.log('timer1')

    Promise.resolve().then(function() {
        console.log('promise1')})},0)

setTimeout(() = >{
    console.log('timer2')

    Promise.resolve().then(function() {
        console.log('promise2')})},0)
Copy the code

Analyze the browser output using what we’ve just learned:

timer1
promise1
timer2
promise2
Copy the code

Is your thinking consistent with the picture below?

Let’s look at the output in Node again

timer1
timer2
promise1
promise2
Copy the code

Node uses Chrome’s V8 parsing engine, and libuv is designed for I/O processing. Libuv is an event-driven cross-platform abstraction layer that encapsulates some low-level features of different operating systems, provides a unified API externally, and the event loop mechanism is also implemented inside it. /deps/uv/ SRC/Unix /core.c Here is an article juejin.cn/post/694570…

int uv_run(uv_loop_t* loop, uv_run_mode mode) {
  int timeout;
  int r;
  int ran_pending;

  r = uv__loop_alive(loop);
  if(! r) uv__update_time(loop);while(r ! =0 && loop->stop_flag == 0) {
    uv__update_time(loop);
    uv__run_timers(loop);
    ran_pending = uv__run_pending(loop);
    uv__run_idle(loop);
    uv__run_prepare(loop);

    timeout = 0;
    if((mode == UV_RUN_ONCE && ! ran_pending) || mode == UV_RUN_DEFAULT) timeout = uv_backend_timeout(loop); uv__io_poll(loop, timeout); uv__run_check(loop); uv__run_closing_handles(loop);if (mode == UV_RUN_ONCE) {
      /* UV_RUN_ONCE implies forward progress: at least one callback must have * been invoked when it returns. uv__io_poll() can return without doing * I/O (meaning: no callbacks) when its timeout expires - which means we * have pending timers that satisfy the forward progress constraint. * * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from * the check. */
      uv__update_time(loop);
      uv__run_timers(loop);
    }

    r = uv__loop_alive(loop);
    if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
      break;
  }

  /* The if statement lets gcc compile it to a conditional store. Avoids * dirtying a cache line. */
  if(loop->stop_flag ! =0)
    loop->stop_flag = 0;

  return r;
}
Copy the code

According to node.js, each event loop consists of six phases, which correspond to the implementation in libuv source code, as shown in the following figure:

  1. Timers stage: This stage performs the callback of timer (setTimeout, setInterval)
  2. I/O Callbacks phase: Perform some system call errors, such as error callbacks for network communication
  3. Idle, prepare: Used only internally
  4. Poll phase: Fetch new I/O events, where node will block under appropriate conditions
  5. Check phase: Perform the setImmediate() callback
  6. Close Callbacks stage: Performs the close event callback of the socket

It is good to focus on the timers, Poll, and Check phases as these are where most asynchronous tasks in daily development are handled.

timers

Timers are the first phase of the event loop. Node checks for expired timers and pushes them back into the timer queue for execution. In fact, Node does not guarantee that the timer will run immediately after the preset time. Because Node’s timer expiration check may not be reliable, it may be affected by other programs running on the machine, or the main thread may not be idle at that point in time. In the code below, for example, setTimeout() and setImmediate() are not executed in an indeterminate order.

setTimeout(() = > {
  console.log('timeout')},0)

setImmediate(() = > {
  console.log('immediate')})Copy the code

But putting them in an I/O callback must lead to setImmediate(), because the poll phase is followed by the check phase. When all phases are executed in sequence once, the Event loop completes a tick. Give it a try:

const fs = require('fs')

fs.readFile('test.txt'.() = > {
  console.log('readFile')
  setTimeout(() = > {
    console.log('timeout')},0)
  setImmediate(() = > {
    console.log('immediate')})})Copy the code

Here’s a picture to illustrate:

Let’s see how the initial example works in Node:

Expansion problem:

The two functions that are often used in Node are more efficient than setTimeout, which is implemented by red-black tree with logn time complexity

process.nextTick() VS setImmediate()

Process.nexttick () will execute between each event stage. Once executed, the next event stage will take place until the nextTick queue is emptied, so recursively calling process.nexttick () will cause I/O starving. For example, the following readFile is complete, but its callback never executes:

const starttime = Date.now() let endtime fs.readFile('text.txt', () => { endtime = Date.now() console.log('finish reading time: ', endtime - starttime) }) let index = 0 function handler () { if (index++ >= 1000) return console.log(`nextTick ${index}`)  process.nextTick(handler) // console.log(`setImmediate ${index}`) // setImmediate(handler) } handler()Copy the code

Running results:

nextTick 1
nextTick 2. nextTick999
nextTick 1000
finish reading time: 170
Copy the code

Instead, setImmediate() runs the result:

setImmediate 1
setImmediate 2
finish reading time: 80. setImmediate999
setImmediate 1000
Copy the code

Reference:

  • Jakearchibald.com/2015/tasks-…
  • www.cnblogs.com/yzfdjzwl/p/…
  • Lynnelv. Making. IO/js – event – lo…
  • Lynnelv. Making. IO/js – event – lo…
  • Juejin. Cn/post / 694570…