Node. js event-driven, node. js event
Node. js event-driven implementation Overview
Although the ECMAScript standard does not (and is not necessary) specify "Events", in the browser, Events Act as an extremely important mechanism, provides JavaScript capabilities to respond to user operations and DOM changes. in js, the asynchronous event-driven model is the foundation of its high concurrency capability.
To better understand the JavaScript event model, I plan to start with Node and browser engine source code and analyze its underlying implementation, I will also organize my analysis into a series of blog posts. On the one hand, I will take notes, and I hope I can communicate with you, analyze and understand the omissions and mistakes. I hope you can make an axe.
Brief Introduction to the event-driven model
There are already many good articles to explain the JavaScript event model. It can be said that this is already a bad topic. Here I just want to write it briefly and provide links to some good articles.
How the program responds to events
Our program responds to external events in the following two ways:
Interrupted
The operating system processes keyboard and other hardware input through interruption. The advantage of this method is that even if there is no multithreading, we can safely execute our code, after the CPU receives the interrupt signal, it automatically redirects to execute the corresponding interrupt processing program. After the processing is complete, the execution environment of the original code is restored and the execution continues. This method requires hardware support and is generally encapsulated by the operating system.
Round Robin
Check whether any event occurs cyclically. If yes, execute the corresponding processing program. This applies to both underlying and upper-layer development.
The Windows window program needs to write the following code in the main thread, which is usually called a message loop:
MSG msg = { };while (GetMessage(&msg, NULL, 0, 0)){ TranslateMessage(&msg); DispatchMessage(&msg);}
The message loop continuously checks whether messages (User UI operations, system messages, etc.) appear. If yes, messages are sent separately and corresponding callback functions are called for processing.
One disadvantage of the polling method is that if time-consuming operations are performed in the message loop of the main thread, the program will not be able to respond to new messages in a timely manner. This is evident in JavaScript, and will be mentioned later and its solution will be explored.
However, JavaScript does not have code similar to a message loop. We simply register an event and wait for it to be called. This is because the browser and Node have implemented the event loop as the execution platform. JavaScript code does not need to be involved in this process, but needs to wait quietly as the caller.
Event loop in Node
View the implementation of event loop through Node source code
Node uses V8 as the JavaScript execution engine and libuv to implement event-driven asynchronous I/O. Its event loop uses the default libuv event loop.
In src/node. cc,
Environment* env = CreateEnvironment( node_isolate, uv_default_loop(), context, argc, argv, exec_argc, exec_argv);
This code sets up a node execution environment. You can see the uv_default_loop () in the third line. This is a function in the libuv library. It initializes the uv library and the default_loop_struct, return the default_loop_ptr pointer to it.
Then, the Node will load the execution environment and complete some configuration operations, and then start the event loop:
bool more;do { more = uv_run(env->event_loop(), UV_RUN_ONCE); if (more == false) { EmitBeforeExit(env); // Emit `beforeExit` if the loop became alive either after emitting // event, or after running some callbacks. more = uv_loop_alive(env->event_loop()); if (uv_run(env->event_loop(), UV_RUN_NOWAIT) != 0) more = true; }} while (more == true);code = EmitExit(env);RunAtExit(env);...
More is used to identify whether to perform the next loop.
Env-> event_loop () will return default_loop_ptr that was previously stored in env. The uv_run function will start the event loop of libuv in the specified UV_RUN_ONCE mode. In this mode, uv_run will process at least one event: this means that if there is no I/O event to be processed in the current event queue, uv_run will block, until there is an I/O event to be processed, or the next timer time is up. If no I/O event or timer event exists, uv_run returns false.
Next, the Node will decide the next operation based on the situation of more:
If more is true, continue to run the next loop.
If more is false, it indicates that no event is waiting for processing. EmitBeforeExit (env); triggers the 'foreout' event of the Process, checks and processes the corresponding processing function, jump out of the loop directly after completion.
Finally, the 'exit 'event is triggered and the corresponding callback function is executed. The Node stops running and some resource release operations will be performed later.
In libuv, Timer events are directly processed in event loop, while I/O events are classified into two types:
Network I/O is a non-blocking I/O solution provided by the system, such as epoll for Linux and IOCP for windows.
There is no (good) system solution for file operations and DNS operations. Therefore, libuv creates a thread pool and blocks I/O in it.
In addition, we can also throw custom functions to the thread pool for running. After the running ends, the main thread executes the corresponding callback function, but Node does not add this function to JavaScript, that is to say, the native Node alone cannot start a new thread in JavaScript for parallel execution.
The above is all the content of this article. I hope you will like it.