1. Opening analysis
In the previous chapter, we learned the basic theoretical knowledge of NodeJS. Understanding this theoretical knowledge is crucial. In the subsequent chapters, we will gradually learn various modules in the official documents. Well, it is time for the protagonist of this article to appear on stage. Global
Let's take a look at the official definition:
Global Objects These objects are available in all modules. Some of these objects aren't actually in the global scope but in the module scope - this will be noted.
These objects are available in all modules. In fact, some objects are not in the global scope scope, but in its module scope -- these will be identified.
In browsers, the top-level scope is the global scope. That means that in browsers if you're in the global scope var something will define a global variable.
In Node this is different. The top-level scope is not the global scope; var something inside a Node module will be local to that module.
I think everyone should not be unfamiliar with the concept of global objects. In the browser, the highest level scope is Global Scope, which means that if you use "var" to define a variable in Global Scope, this variable will be defined as Global Scope.
But it is different in NodeJS. The highest level of Scope is not Global Scope. In a Module, a variable is defined using "var" and this variable is only in the Scope of this Module.
In NodeJS, variables, functions, or methods defined in a module are only available in that module, but can be passed to outside the module through the use of the exports object.
However, in Node.js, there is still a global scope, that is, you can define variables, functions, or classes that do not require loading of any modules.
At the same time, some global methods and global class Global objects are predefined in advance, which are the global namespaces in NodeJS. Any global variables, functions or objects are attribute values of the object.
In the REPL running environment, you can observe the details in the Global object through the following statement, see the figure below:
I will talk about the relevant attribute value objects mounted on the Global object one by one below.
(1), Process
process {Object} The process object.See the process object section.
process {object} This is a process object. I will explain in detail in the subsequent chapters, but here I will first take out an API to talk about it.
process.nextTick(callback)
On the next loop around the event loop call this callback. This is not a simple alias to setTimeout(fn, 0), it's much more efficient. It typically runs before any other I/O events fire, but there are some exceptions. See process.maxTickDepth below.
Callback callback function in the next loop of the event loop. This is not a simple alias for the setTimeout(fn, 0) function, because it is much more efficient.
This function can call our callback function before any I/O. If you want to perform certain operations after object creation and before the I/O operation occurs, this function is very important to you.
Many people don’t understand the usage of process.nextTick() in Node.js. Let’s take a look at what process.nextTick() is and how to use it.
Node.js is single-threaded. Except for system IO, only one event will be processed at the same time during its event polling process. You can think of event polling as a large queue, at each point in time, the system will only process one event.
Even if your computer has multiple CPU cores, you cannot handle multiple events in parallel at the same time. But this feature makes node.js suitable for processing I/O applications, but not for CPU computing applications.
In each I/O application, you only need to define a callback function for each input and output, and they will be automatically added to the event polling processing queue.
When the I/O operation is completed, this callback function will be triggered. The system will then continue to process other requests.
In this processing mode, process.nextTick() means to define an action and let this action be executed at the point in time when the next event polls. Let's take a look at an example. In the example, there is a foo() that you want to call at the next point in time, you can do this:
The code copy is as follows:
function foo() {
console.error('foo');
}
process.nextTick(foo);
console.error('bar');
Run the above code and you will see that the output of "bar" is in front of "foo". This verifies the above statement that foo() runs at the next point in time.
The code copy is as follows:
bar
foo
You can also use the setTimeout() function to achieve the same execution effect:
The code copy is as follows:
setTimeout(foo, 0);
console.log('bar');
However, in terms of internal processing mechanism, process.nextTick() and setTimeout(fn, 0) are different. process.nextTick() is not a simple delay, it has more features.
More precisely, the call defined by process.nextTick() creates a new substack. In the current stack, you can perform as many operations as you like. But once netxTick is called, the function must be returned to the parent stack. Then the event polling mechanism waits for new events to be processed again. If nextTick is found, a new stack will be created.
Let's take a look at what circumstances to use process.nextTick():
Cross-execute CPU operation-intensive tasks in multiple events:
In the following example, there is a compute(). We hope that this function will be executed as continuously as possible to perform some operation-intensive tasks.
But at the same time, we also hope that the system will not be blocked by this function and will also be able to respond and handle other events. This application pattern is like a single threaded web service server. Here we can use process.nextTick() to cross-execute compute() and normal event response.
The code copy is as follows:
var http = require('http');
function compute() {
// performs complicated calculations continuously
// ...
process.nextTick(compute);
}
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World');
}).listen(5000, '127.0.0.1');
compute();
In this mode, we do not need to call compute() recursively. We only need to use process.nextTick() to define compute() to execute at the next point in time in the event loop.
During this process, if a new http request comes in, the event loop mechanism will first process the new request and then call compute().
On the contrary, if you put compute() in a recursive call, the system will be blocked in compute() and cannot process new http requests. You can try it yourself.
Of course, we cannot get the real benefits of parallel execution under multiple CPUs through process.nextTick(), which is just to simulate the same application being executed in segments on the CPU.
(2), Console
console {Object} Used to print to stdout and stderr.See the stdio section.
Console {Object} is used to print to standard output and error output. See the following test:
The code copy is as follows:
console.log("Hello Bigbear !");
for(var i in console){
console.log(i+" "+console[i]);
}
The following output results will be obtained:
The code copy is as follows:
var log = function () {
process.stdout.write(format.apply(this, arguments) + '/n');
}
var info = function () {
process.stdout.write(format.apply(this, arguments) + '/n');
}
var warn = function () {
writeError(format.apply(this, arguments) + '/n');
}
var error = function () {
writeError(format.apply(this, arguments) + '/n');
}
var dir = function (object) {
var util = require('util');
process.stdout.write(util.inspect(object) + '/n');
}
var time = function (label) {
times[label] = Date.now();
}
var timeEnd = function (label) {
var duration = Date.now() - times[label];
exports.log('undefined: NaNms', label, duration);
}
var trace = function (label) {
// TODO probably can to do this better with V8's debug object once that is
// exposed.
var err = new Error;
err.name = 'Trace';
err.message = label || '';
Error.captureStackTrace(err, arguments.callee);
console.error(err.stack);
}
var assert = function (expression) {
if (!expression) {
var arr = Array.prototype.slice.call(arguments, 1);
require('assert').ok(false, format.apply(this, arr));
}
}
Through these functions, we basically know what NodeJS has added to the global scope. In fact, the relevant APIs on the Console object only encapsulate the "stdout.write" on the Process object and hang it on the global object.
(3), exports and module.exports
In NodeJS, there are two scopes, divided into global scope and module scope
The code copy is as follows:
var name = 'var-name';
name = 'name';
global.name='global-name';
this.name = 'module-name';
console.log(global.name);
console.log(this.name);
console.log(name);
We see that var name = 'var-name';name = 'name'; is a defined local variable;
global.name='global-name'; defines a name attribute for the global object.
And this.name = 'module-name'; defines a name attribute for the module object
So let's verify it, save the following as test2.js, and run it
The code copy is as follows:
var t1 = require('./test1');
console.log(t1.name);
console.log(global.name);
As can be seen from the results, we successfully imported the test1 module and ran the test1 code because global.name is output in test2.
t1.name is defined in the test1 module through this.name, indicating that this points to the module scope object.
A little difference between exports and module.exports
Module.exports is the real interface, and exports is just an auxiliary tool for it. The final return to the call is Module.exports instead of exports.
All properties and methods collected by exports are assigned to Module.exports . Of course, there is a prerequisite for this, that is, Module.exports本身不具备任何属性和方法。
如果, Module.exports已经具备一些属性和方法,那么exports收集来的信息将被忽略。
Take a chestnut:
Create a new file bb.js
The code copy is as follows:
exports.name = function() {
console.log('My name is Big Bear!') ;
} ;
Create a test file test.js
The code copy is as follows:
var bb= require('./bb.js');
bb.name(); // 'My name is Big Bear! '
Modify bb.js as follows:
The code copy is as follows:
module.exports = 'BigBear!' ;
exports.name = function() {
console.log('My name is Big Bear!') ;
} ;
Refer to execute bb.js again
The code copy is as follows:
var bb= require('./bb.js');
bb.name(); // has no method 'name'
From this we can see that your module does not necessarily have to return an "institized object". Your module can be any legal javascript object --boolean, number, date, JSON, string, function, array, etc.
(4), setTimeout, setInterval, process.nextTick, setImmediate
The following is in the form of a summary
Nodejs is characterized by event-driven, high concurrency generated by asynchronous I/O. The engine that produces this feature is an event loop. Events are classified into corresponding event observers, such as idle observers, timer observers, I/O observers, etc. Each loop of the event loop is called a Tick. Each Tick takes events out of the event observers in sequence for processing.
The timer created when calling setTimeout() or setInterval() will be placed in the red and black tree inside the timer observer. Every time you tick, it will check whether the timer has exceeded the timing time from the red and black tree. If it exceeds the timing, the corresponding callback function will be executed immediately. setTimeout() and setInterval() are both used by timers. The difference is that the latter is triggered repeatedly, and because the time setting is too short, the processing after the previous trigger will be triggered immediately after the previous trigger is completed.
Since the timer is a timeout trigger, this will reduce the trigger accuracy. For example, the timeout time set with setTimeout is 5 seconds. When the event loop runs through a task in the 4th second and its execution time is 3 seconds, the setTimeout callback function will expire for 2 seconds, which is the reason for the reduction in accuracy. And because the timer and judgment triggers are saved using red and black trees and iterative methods, it is a waste of performance.
All callback functions set using process.nextTick() will be placed in the array, and all will be executed immediately on the next time you tick. This operation is lightweight and has high time accuracy.
The callback function set by setImmediate() is also called on the next Tick. The difference between it and process.nextTick() is two points:
1. The priority of execution of the observer they belong to is different. Process.nextTick() belongs to the idle observer, setImmediate() belongs to the check observer, and idle's priority>check.
2. The callback function set by setImmediate() is placed in a linked list, and only one callback in the linked list is executed at each time. This is to ensure that every Tick can be executed quickly.
Second, let's summarize
1. Understand the meaning of the existence of Global objects
2. A little difference between exports and module.exports
3. What is the underlying layer of Console built (high-level encapsulation of Process objects)
4. The difference between setTimeout, setInterval, process.nextTick, setImmediate
5. Two scopes in NodeJS