Home  >  Article  >  Web Front-end  >  Understanding events and event loop in nodejs

Understanding events and event loop in nodejs

青灯夜游
青灯夜游forward
2020-12-08 17:40:073211browse

Understanding events and event loop in nodejs

Related recommendations: "nodejs Tutorial"

Friends who are familiar with javascript should have used events, such as mouse movement, mouse click, Keyboard input and so on. We listen to these events in javascript to trigger corresponding processing.

There are also events in nodejs, and there is also a special events module for specialized processing.

Simultaneous events and event loops are also very important concepts for building asynchronous IO in nodejs.

Today we will learn more about it.

Events

nodejs provides a special module for events: lib/events.js.

Remember when we were talking about using nodejs to build a web server?

const server = http.createServer((req, res) => {
  res.statusCode = 200
  res.setHeader('Content-Type', 'text/plain')
  res.end('welcome to www.flydean.com\n')
})

Here, each request will trigger the request event.

The core API of nodejs is based on asynchronous event-driven architecture, so there are a lot of events in nodejs.

For example: net.Server will trigger an event every time there is a new connection, fs.ReadStream will trigger an event when a file is opened, and stream will trigger an event when the data is readable.

Let’s take a look at how to build a nodejs event:

const EventEmitter = require('events')
const eventEmitter = new EventEmitter()

There are two commonly used methods for events, namely on and emit.

on is used to listen for events, and emit is used to trigger events.

eventEmitter.on('fire', () => {
  console.log('开火')
})

eventEmitter.emit('fire')

emit can also take parameters. Let’s look at the next parameter:

eventEmitter.on('fire', who => {
  console.log(`开火 ${who}`)
})

eventEmitter.emit('fire', '美帝')

Let’s look at the two parameters:

eventEmitter.on('fire', (who, when) => {
  console.log(`开火 ${who} ${when}`)
})

eventEmitter.emit('fire', '川建国','now')

By default, EventEmitter starts with All listeners are called synchronously in the order they were registered. This ensures correct ordering of events and helps avoid race conditions and logic errors.

If asynchronous execution is required, you can use setImmediate() or process.nextTick() to switch to asynchronous execution mode.

eventEmitter.on('fire', (who, when) => {
    setImmediate(() => {
      console.log(`开火 ${who} ${when}`);
  });
})

eventEmitter.emit('fire', '川建国','now')

In addition, events also support several other methods:

once(): Add a single listener

removeListener() / off(): From Remove event listeners from events

removeAllListeners(): Remove all event listeners

Event loop

We know that nodejs code runs in a single-threaded environment Yes, only one thing will be dealt with at a time.

This processing method avoids the problem of data synchronization in a multi-threaded environment and greatly improves processing efficiency.

The so-called event loop means that in a program cycle, after the processor has processed the events of this cycle, it will enter the next event cycle and process the events of the next event cycle. This is a cycle after cycle. .

Blocking of event loop

If the processing of an event is blocked during event processing, it will affect the execution of other events, so we can see that in JS , almost all IO is non-blocking. This is also why there are so many callbacks in JavaScript.

Event loop example

Let’s look at a simple event loop example:

const action2 = () => console.log('action2')

const action3 = () => console.log('action3')

const action1 = () => {
    console.log('action1')
    action2()
    action3()
}

action1()

The above code output:

action1
action2
action3

Stack and message queue

We know that calls between functions are implemented through the stack. In the above example, our calling sequence is also implemented through the stack.

But not all methods in the function will be pushed onto the stack, and some methods will be put into the message queue.

Let’s give another example:

const action2 = () => console.log('action2')

const action3 = () => console.log('action3')

const action1 = () => {
    console.log('action1')
    setTimeout(action2, 0)
    action3()
}

action1()

The result of running the above code is:

action1
action3
action2

The result is different. This is because settimeout triggers the timer. When the timer expires, the callback function will be placed in the message queue to be processed instead of being placed on the stack.

The event loop will prioritize events in the stack. Only when there is no data in the stack will it switch to consuming events in the message queue.

Although the timeout time of setTimeout in the above example is 0, it still has to wait until action3 is executed before it can be executed.

Note that the timeout in setTimeout does not wait in the current thread. It is called by the browser or other JS execution environment.

Job queue and promise

Promise in ES6 introduces the concept of job queue. Using the job queue will execute the result of the asynchronous function as soon as possible instead of placing it at the end of the call stack.

For example:

const action2 = () => console.log('action2')

const action3 = () => console.log('action3')

const action1 = () => {
    console.log('action1')
    setTimeout(action2, 0)
    new Promise((resolve, reject) =>
        resolve('应该在action3之后、action2之前')
    ).then(resolve => console.log(resolve))
    action3()
}

action1()

Output result:

action1
action3
应该在action3之后、action2之前
action2

This is because the Promise resolved before the end of the current function will be executed immediately after the current function.

That is to say, the stack is executed first, then the job queue is executed, and finally the message queue is executed.

process.nextTick()

Let me first give you a definition called tick. A tick refers to an event cycle. Process.nextTick() refers to calling this function before the next event loop tick starts:

process.nextTick(() => {
  console.log('i am the next tick');
})

So nextTick must be faster than the setTimeout of the message queue.

setImmediate()

nodejs provides a setImmediate method to execute code as quickly as possible.

setImmediate(() => {
  console.log('I am immediate!');
})

The function in setImmediate will be executed in the next iteration of the event loop.

The functions of setImmediate() and setTimeout(() => {}, 0) are basically similar. They will all be run on the next iteration of the event loop.

setInterval()

If you want to execute certain callback functions regularly, you need to use setInterval.

setInterval(() => {
  console.log('每隔2秒执行一次');
}, 2000)

要清除上面的定时任务,可以使用clearInterval:

const id = setInterval(() => {
  console.log('每隔2秒执行一次');
}, 2000)

clearInterval(id)

注意,setInterval是每隔n毫秒启动一个函数,不管该函数是否执行完毕。

如果一个函数执行时间太长,就会导致下一个函数同时执行的情况,怎么解决这个问题呢?

我们可以考虑在回调函数内部再次调用setTimeout,这样形成递归的setTimeout调用:

const myFunction = () => {
  console.log('做完后,隔2s再次执行!');

  setTimeout(myFunction, 2000)
}

setTimeout(myFunction, 2000)

更多编程相关知识,请访问:编程视频!!

The above is the detailed content of Understanding events and event loop in nodejs. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:flydean. If there is any infringement, please contact admin@php.cn delete