The first article in this series introduced why we need asynchronous I/O in terms of user experience and resource allocation. This article focuses on asynchronous I/O support at the operating system level. The second article covered asynchronous I/O support at the operating system level, and the difference between asynchronous and non-blocking. The third article introduces the implementation of nodeJS asynchronous I/O.

Let’s review how asynchronous I/O is implemented:

  1. The JavaScript layer makes API calls such as fs.open()
  2. Nodejs internal JavaScript layer core module that calls c++ built-in modules that rely on libuv to call the underlying operating system methods
  3. When a method is called, it wraps a request object, putting the callback into the object’s properties
  4. Add the request object to the I/O thread pool and wait for execution
  5. When the thread is idle, I/O operations are performed
  6. After execution, the result is placed on req->result, and the method is called to tell the thread to release and modify the state
  7. The nodeJS event loop checks the task execution status in the I/O thread pool and adds the task to the QUEUE of THE I/O observer during a single Tick execution
  8. The subsequent Tick execution will ask the I/O observer if there are any events to execute
  9. If so, the I/O observer requests the result and callback object to execute, and the callback is complete

SetTimeout (), setInterval(), setImmediate(), and process.nexttick ()

The timer

SetTimeout and setInterval are identical to the browser apis. They execute scheduled tasks single and multiple times, respectively. They work in a similar way to asynchronous I/O, but do not involve an I/O thread pool.

  1. Call the setTimeout
  2. Create a timer object and place it in the timer observer’s red-black tree
  3. In the event loop, each time the Tick is executed, the timer object is iteratively removed from the timer observer and red-black tree to check whether the timer time is exceeded
  4. If it does, an event is generated and its callback is executed immediately

The problem with timers is that they are not accurate (within tolerance), and although events loop quickly, if one loop takes too much time, the next loop may run out of time. For example, a task is executed after 10ms, but a task occupies 5ms CPU time slice after 9ms. Then, when it is the turn of the timer to execute, the time has passed 4ms.

process.nextTick

Process. nextTick simply queues the callback function and takes it out the next time it ticks, making it suitable for an asynchronous task to execute immediately.

We might have used setTimeout(fn, 0) before, which was actually bad, because setTimeout uses red-black tree, operation time complexity is O(LG (n)), nextTick is O(1), nextTick is more efficient

setImmediate

SetImmediate is similar to nextTick, so let’s take a look at the sequence of their execution:

process.nextTick(function () { 
    console.log('nextTick delay execution ');
});
setImmediate(function () {
    console.log('setImmediate Delays execution '); 
});
console.log('Normal execution');
Copy the code

The execution of nextTick takes precedence over setImmediate because the loop has a certain sequence of events. NextTick is the idle observer and setImmediate is the Check mediate. The priority of the observer is: Idle Observer > I/O Observer > Check Observer.

Implementatively, nextTick stores the callbacks in an array, and each loop executes all of the callbacks in the array. SetImmediate stores the callbacks in a linked list and executes the first one in the list at a time. For example:

// Add two nextTick() callbacks
process.nextTick(function () {
    console.log('nextTick delay execution 1'); 
});

process.nextTick(function () { 
    console.log('nextTick delay execution 2');
});  
// Add two setImmediate() callbacks
setImmediate(function () {
    console.log('setImmediate Delays execution 1'); // Enter the next loop
    process.nextTick(function () {
        console.log('Strong insertion'); 
    });
});
setImmediate(function () {
    console.log('setImmediate Delays execution 2'); 
});

console.log('Normal execution');

// Normal execution
// nextTick delays executing 1
// nextTick delays executing 2
// setImmediate delays execution 1
// Strong insertion
// setImmediate delays execution 2
Copy the code

Normal execution

First Tick: Executes all callbacks in the saved array in nextTick, and setImmediate saves the first callback in the linked list

Second Tick: Executes all callbacks in the saved array in nextTick, and setImmediate saves the second callback in the linked list

SetImmediate setImmediate is designed so that each loop ends quickly, preventing it from taking up too much CPU and blocking subsequent I/O calls

The setTimeout principle, Process. nextTick, and setImmediate mediate mediate mediate