• 为什么 CommonJS 相互引用没有产生类似“死锁”的问题?
  • 发布于 2个月前
  • 383 热度
    0 评论
我们可以发现 CommonJS 模块相互引用时,没有产生类似死锁的问题。关键在 Module._load 函数里,具体源代码在这里。Module._load 函数主要做了下面这些事情:
1.检查缓存,如果缓存存在且已经加载,直接返回缓存,不做下面的处理
2.如果缓存不存在,新建一个 Module 实例
3.将这个 Module 实例放到缓存中
4.通过这个 Module 实例来加载文件
5.返回这个 Module 实例的 exports

其中的关键在 放到缓存中 与 加载文件 的顺序,在我们的 MyModule 中,也就是这两行代码:
MyModule._cache[filename] = module;
module.load(filename);
回到上面循环加载的例子中,解释一下到底发生了什么:
当 app.js 加载 a.js 时,Module 会检查缓存中有没有 a.js,发现没有,于是 new 一个 a.js 模块,并将这个模块放到缓存中,再去加载 a.js 文件本身。
在加载 a.js 文件时,Module 发现第一行是加载 b.js,它会检查缓存中有没有 b.js,发现没有,于是 new 一个 b.js 模块,并将这个模块放到缓存中,再去加载 b.js 文件本身。

在加载 b.js 文件时,Module 发现第一行是加载 a.js,它会检查缓存中有没有 a.js,发现存在,于是 require 函数返回了缓存中的 a.js。但是其实这个时候 a.js 根本还没有执行完,还没走到 module.exports 那一步,所以 b.js 中 require('./a.js') 返回的只是一个默认的空对象。所以最终会报 setA is not a function 的异常。

说到这里,那如何设计会导致“死锁”呢?其实也很简单 —— 将 放到缓存中 与 加载文件 的执行顺序互换,在我们的 MyModule 代码中,也就是这样写:
module.load(filename);
MyModule._cache[filename] = module;
这样互换一下,再执行 demo03,我们发现异常如下:

我们发现这样写会死锁,最终导致 JS 报栈溢出异常。
用户评论