nodejs event loop and multi-process (2) - fs and setTimeout, setImmediate relationship & event loop is asynchronous operation & process.nextTick blocking IO operation & app.on subscription, app.emit trigger

nodejs event loop and multi-process (2) - fs and setTimeout, setImmediate relationship & event loop is asynchronous operation & process.nextTick blocking IO operation & app.on subscription, app.emit trigger

Chapter 3 nodejs event loop

When Node.js starts, the event loop will be initialized, and each event loop will contain the following six cycle stages. The nodejs event loop is completely different from the browser's event loop.

When Node.js starts, it initializes the event loop, processes the provided input script (or drops into the REPL, which is not covered in this document) which may make async API calls, schedule timers, or call process.nextTick(), then begins processing the event loop.

insert image description here

Note: Each box in the figure is called a "phase" of the event loop , and these 6 phases are a round of event loop.

Phase overview

  • timers : This phase executes those callback functions scheduled by setTimeout()and .setInterval()

  • I/O callbacks (I/O callbacks) : This phase will execute almost all callback functions, except close callbacks (close callbacks) and those callbacks scheduled by timers .setImmediate()

    setImmediate is approximately equal to setTimeout(cb,0)

  • idle (idle), prepare : this phase is only used internally

  • poll (polling) : Retrieve new I/O events; Node will block at this stage when appropriate

  • check (check): setImmediate()The set callback will be called at this stage

  • close callbacks (close event callback): callbacks such as socket.on('close', ...)this are called during this phase

Between each run of the event loop, Node.js checks to see if it is waiting for asynchronous I/O or a timer, and shuts down automatically if it is not.

If the event loop enters the poll phase and the code does not set a timer, the following will happen:

  • If the poll queue is not empty, the event loop will execute the callback in the queue synchronously until the queue is empty, or the executed callback reaches the system upper limit;
  • If the poll queue is empty, the following will happen:
    • If the code has been set with callback by setImmediate(), the event loop will end the poll phase and enter the check phase, and execute the queue in the check phase (the queue in the check phase is set by setImmediate)
    • If the code does not set setImmediate(callback), the event loop will block at this stage and wait for the callbacks to join the poll queue, and execute it immediately once it arrives

If the event loop enters the poll phase, and the code sets the timer:

  • If the poll queue enters the empty state (that is, the poll phase is idle), the event loop will check the timers. If one or more timers have arrived, the event loop will enter the timers phase in a circular order and execute the timer queue.

code execution 1

path.resolve()method resolves a path or sequence of path fragments to an absolute path.

fs.readFileRead the entire contents of the file asynchronously.

__dirnameAlways point to the absolute path of the executed folder

schematic diagram

insert image description here

the code

demo.js

var fs = require('fs');
var path = require('path');

function someAsyncOperation (callback) {
    
    
  // 花费2毫秒
  fs.readFile(path.resolve(__dirname, '/read.txt'), callback);
}

var timeoutScheduled = Date.now();
var fileReadTime = 0;

setTimeout(function () {
    
    
  var delay = Date.now() - timeoutScheduled;
  console.log('setTimeout: ' + (delay) + "ms have passed since I was scheduled");
  console.log('fileReaderTime',fileReadtime - timeoutScheduled);
}, 10);

someAsyncOperation(function () {
    
    
  fileReadtime = Date.now();
  while(Date.now() - fileReadtime < 20) {
    
     // 卡住20ms

  }
});

// 关注: setTimeout它的回调执行时机, fs.readFile 回调的执行时间
// 事件循环调度是异步操作,重点关注异步代码的执行时机

Open command terminal

node ./demo.js

print display

setTimeout: 22ms have passed since I was scheduled
fileReaderTime 2

code execution 2

Example test run file times

readfile.js

var fs = require('fs')
var path = require('path')

console.time('file')
fs.readFile(path.resolve(__dirname,'./同目录测试文件.txt'),'utf8',()=>{
    
    
   console.timeEnd('file')
})

Note: __dirname is two underscores

open terminal

node ./readfile.js

show

file:11.468ms

example

var fs = require('fs');

function someAsyncOperation (callback) {
    
    
  var time = Date.now();
  // 花费9毫秒
  fs.readFile('/path/to/xxxx.pdf', callback);
}

var timeoutScheduled = Date.now();
var fileReadTime = 0;
var delay = 0;

setTimeout(function () {
    
    
  delay = Date.now() - timeoutScheduled;
}, 5);

someAsyncOperation(function () {
    
    
  fileReadtime = Date.now();
  while(Date.now() - fileReadtime < 20) {
    
    

  }
  console.log('setTimeout: ' + (delay) + "ms have passed since I was scheduled");
  console.log('fileReaderTime',fileReadtime - timeoutScheduled);
});
// 关注: setTimeout它的回调执行时机, fs.readFile 回调的执行时间
// 事件循环调度是异步操作,重点关注异步代码的执行时机

example

var fs = require('fs');
var path = require('path');

function someAsyncOperation (callback) {
    
    
  // 花费9毫秒
  fs.readFile(path.resolve(__dirname, '/read.txt'), callback);
}

var timeoutScheduled = Date.now();
var fileReadTime = 0;

setTimeout(function () {
    
    
  var delay = Date.now() - timeoutScheduled;
  console.log('setTimeout: ' + (delay) + "ms have passed since I was scheduled");
  console.log('fileReaderTime',fileReadtime - timeoutScheduled);
}, 5);

someAsyncOperation(function () {
    
    
  fileReadtime = Date.now();
  while(Date.now() - fileReadtime < 20) {
    
     // 卡住20ms

  }
});

// 关注: setTimeout它的回调执行时机, fs.readFile 回调的执行时间
// 事件循环调度是异步操作,重点关注异步代码的执行时机

code execution 3

In nodejs, setTimeout(demo, 0) === setTimeout(demo, 1)

In the browser setTimeout(demo, 0) === setTimeout(demo, 4)

setTimeout(function timeout () {
    
    
  console.log('timeout');
},1);

setImmediate(function immediate () {
    
    
  console.log('immediate');
});
// 执行顺序是不确定的,setImmediate它有时候是1ms之前执行,有时候又是1ms之后执行?

Because the start of the event loop also takes time, it may have exceeded 1ms until the poll stage is executed, and setTimeout will be executed first at this time. Otherwise setImmediate executes first

var path = require('path');
var fs = require('fs');

fs.readFile(path.resolve(__dirname, '/read.txt'), () => {
    
    
    setImmediate(() => {
    
    
        console.log('setImmediate');
    })
    
    setTimeout(() => {
    
    
        console.log('setTimeout')
    }, 0)
});
// 执行顺序是确定的  setImmediate在前,setTimeout在后

process.nextTick

process.nextTick() is not executed in any stage of the event loop, but is executed in the middle of each stage switching , that is, before switching from one stage to the next stage.

command terminal

node file name

var fs = require('fs');

fs.readFile(__filename, () => {
    
    
  setTimeout(() => {
    
    
    console.log('setTimeout');
  }, 0);
  setImmediate(() => {
    
    
    console.log('setImmediate');
    process.nextTick(()=>{
    
    
      console.log('nextTick3');
    })
  });
  process.nextTick(()=>{
    
    
    console.log('nextTick1');
  })
  process.nextTick(()=>{
    
    
    console.log('nextTick2');
  })
});
// 执行顺序是 —— nextTick1、nextTick2、setImmediate、nextTick3、setTimeout

example

var fs = require('fs')
var path = require('path')

fs.readFile(path.resolve(__dirname, '/read.txt'), () => {
    
    
  setTimeout(() => {
    
    
    console.log('setTimeout')
  }, 0)
  setImmediate(() => {
    
    
    console.log('setImmediate')
    process.nextTick(() => {
    
    
      console.log('nextTick3')
    })
  })
  process.nextTick(() => {
    
    
    console.log('nextTick1')
  })
  process.nextTick(() => {
    
    
    console.log('nextTick2')
  })
})
// 执行顺序是 —— nextTick1、nextTick2、setImmediate、nextTick3、setTimeout

design reason

Allows developers process.nextTick()to block I/O operations through recursive calls.

nextTick application scenario

  1. Interleave CPU-intensive tasks across multiple events:
var http = require('http');

function compute() {
    
    
    
    process.nextTick(compute);//
}

http.createServer(function(req, res) {
    
      // 服务http请求的时候,还能抽空进行一些计算任务
     res.writeHead(200, {
    
    'Content-Type': 'text/plain'});
     res.end('Hello World');
}).listen(5000, '127.0.0.1');

compute();

In this mode, we don't need to call compute() recursively, we just need to use process.nextTick() in the event loop to define compute() to be executed at the next time point. During this process, if a new http request comes in, the event loop mechanism will process the new request first, and then call compute(). Conversely, if you put compute() in a recursive call, the system will always be blocked in compute() and cannot process new http requests.

  1. The principle of maintaining asynchronous execution of callback functions

When you define a callback function for a function, you make sure that the callback is executed asynchronously. Let's look at an example where the callback violates this principle:

function asyncFake(data, callback) {
    
            
    if(data === 'foo') callback(true);
    else callback(false);
}

asyncFake('bar', function(result) {
    
    
    // this callback is actually called synchronously!
});

Why is this bad? Let's look at a piece of code in the Node.js documentation:

var client = net.connect(8124, function() {
    
     
    console.log('client connected');
    client.write('world!\r\n');
});

In the above code, if for some reason net.connect() becomes synchronous, the callback function will be executed immediately, so the variables written by the callback function to the client will never be initialized.

In this case, we can use process.nextTick() to change the above asyncFake() to asynchronous execution:

function asyncReal(data, callback) {
    
    
    process.nextTick(function() {
    
    
        callback(data === 'foo');       
    });
}
  1. used in event triggering

    EventEmitter has two core methods, on and emit. Node comes with publish/subscribe mode

var EventEmitter = require('events').EventEmitter;

function StreamLibrary(resourceName) {
    
     
    this.emit('start');
}
StreamLibrary.prototype.__proto__ = EventEmitter.prototype;   // inherit from EventEmitter

var stream = new StreamLibrary('fooResource');

stream.on('start', function() {
    
    
    console.log('Reading has started');
});

function StreamLibrary(resourceName) {
    
          
    var self = this;

    process.nextTick(function() {
    
    
        self.emit('start');
    });  // 保证订阅永远在发布之前

    // read from the file, and for every chunk read, do:        
    
}

example

event.js

var EventEmitter = require('events').EventEmitter;

class App extends EventEmitter {
    
    
    
}

var app = new App();

app.on('start', () => {
    
      // on 订阅 
    console.log('start');
});

app.emit('start');  // emit 触发
console.log('111111111111')  // emit是同步的方法。

// 执行顺序是——  start、111111111111
// 为什么是同步的?方便处理逻辑,比如:新增和删除    setTimeout , setImmedate, nextTick这三个是异步api

example

event.js

var EventEmitter = require('events').EventEmitter;

class App extends EventEmitter {
    
    
    
}

var app = new App();

app.on('start', () => {
    
      // on 订阅 
    console.log('start');
});

app.emit('start');  // emit 触发
console.log('111111111111')  // emit是同步的方法。

// 执行顺序是——  start、111111111111
// 为什么是同步的?方便处理逻辑,比如:新增和删除    setTimeout , setImmedate, nextTick这三个是异步api

Guess you like

Origin blog.csdn.net/weixin_44867717/article/details/131407853