CommonJS模块规范
模块引用
math 1
+ ```var a = require('./a.js');
b 1
2* 模块定义
+ ```exports.add = function() {}
模块标识
传递给require()方法的参数
模块的实现
- 优先从缓存加载
- 模块路径的生成规则:从当前路径下的node_modules,沿文件路径逐级向上,知道根路径下的node_modules
模块的编译
JS文件的编译
function()
会被node编译成(function(exports, require, module, __filename, __dirname))
这就是为什么每个模块都存在exports, require, module这些变量的原因exports
是通过形参的方式传入的,直接修改不会有任何作用,eg. `exports = function() {}` 请使用`module.exports = []`
C/C++模块的编译
- node通过process.dlopen()进项加载和执行,不需要编译,
- 通过libuv兼容windows和*nix平台
- 效率高,编写门槛高
JSON文件的编译
- 通过JSON.parse()得到对象
Node中的异步
- JavaScript是单线程的,Node是多线程的
- 除了用户代码无法并行执行外,所有的I/O(磁盘IO和网络IO)则是可以并行起来的
非I/O的异步API
setTimeout()
- 创建的定时器被插入到定时器观察者内部的一个红黑树中。每次tick执行时会从该树中取出定时器对象,检查是否超时。如果超过就形成一个事件,回调函数立即执行
- 复杂度O(lgn)
setInterval()
- 同setTimeout(),不同在于重复性检测和执行,
- 复杂度O(lgn)
process.nextTick()
- 调用时,将回调函数放入队列中,在下一次tick时取出执行,
- 复杂度O(1)
- 属于idle观察者
- 回调函数保存在一个数组中,每次循环中会将数组中的回调函数全部执行完
setImmediate()
- 属于check观察者
- 回调函数保存在链表中,每次循环中执行链表中的一个回调函数
观察者优先级顺序:
- idle观察者 > I/O观察者 > check观察者
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 加入2个nextTick()回调函数
process.nextTick(function() {
console.log('next tick 1');
});
process.nextTick(function() {
console.log('next tick 2');
});
// 加入2个setImmediate()回调函数
setImmediate(function () {
console.log('set immediate 1');
// 进入下次循环
process.nextTick(function() {
console.log('强制插入');
});
});
setImmediate(function () {
console.log('set immediate 2');
});
console.log('正常执行');
1 | 执行结果: |
当第一个setImmediate回调函数执行后,并没有立即执行第二个,而是进入了下一个循环,再次按process.nextTick优先,setImmediate其次的顺序执行。
这么设计的原因:是为了保证每次循环都能较快的执行结束,防止CPU占用过多而阻塞后续I/O调用
事件循环是实现异步的核心
异步编程解决方案
事件发布/订阅模式
1
2
3
4
5emitter.on('event1', function(message) { // 订阅
console.log(message);
});
emitter.emit('event1', 'I’m message!'); // 发布
// once: 执行一次就将监视器移除,保证回调只会被执行一次Promise/Deferred模式
流程控制库