读书笔记之【深入浅出nodejs】

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
2
3
4
5
6
7
执行结果:
正常执行
next tick 1
next tick 2
set immediate 1
强制插入
set immediate 2

当第一个setImmediate回调函数执行后,并没有立即执行第二个,而是进入了下一个循环,再次按process.nextTick优先,setImmediate其次的顺序执行。
这么设计的原因:是为了保证每次循环都能较快的执行结束,防止CPU占用过多而阻塞后续I/O调用

事件循环是实现异步的核心

异步编程解决方案

  • 事件发布/订阅模式

    1
    2
    3
    4
    5
    emitter.on('event1', function(message) { // 订阅
    console.log(message);
    });
    emitter.emit('event1', 'I’m message!'); // 发布
    // once: 执行一次就将监视器移除,保证回调只会被执行一次
  • Promise/Deferred模式

  • 流程控制库