文 / Victor - AfterShip
译 / 吴天成 - AfterShip
问题描述
以下面这种方式,写单例很容易:
let someModule
async getSomeModule() {
if (!someModule) {
someModule = await someAsyncOperationsToInitializeModule()
}
return someModule
}
module.exports = getSomeModule
通常以这种方式使用它:
// in async function
const getSomeModule = require('./getSomeModule')
const someModule = await getSomeModule()
除非你希望将模块的加载延迟到初次运行时,否则不鼓励这种方式。
因为,这将带来很多没必要的分支代码(例如,if statement ),实际上我们希望避免这种代码。而且使用 let
语法将会中断静态代码分析,导致 IDE 不能正确推导出 someModule
的类型。
解决方案 A
请注意,node 的模块系统默认为单例(模块在第一次required的时候将会被缓存[1])。所以一旦一个 promise
被 resolved
并导出,无论谁 require
(加载) 模块,它将始终返回第一次 resolved
的结果。
以下是只使用const
来实现的方式:
// NodeJs 方式的 async 单例
// someAsyncOperationsToInitializeModule 为 async function
// 注意,此处执行函数,未 await
const someModule = someAsyncOperationsToInitializeModule()
module.exports = someModule
2 行代码,就够了。
你该如何使用这个模块呢?
// in async function
// 不要用 "await getSomeModule()", 你不需要 `()`
const getSomeModule = require('./getSomeModule')
const someModule = await getSomeModule
someModule
的值绝对与【问题描述】中提到的代码运行结果完全相同。
你可能会注意到文件名最好更改为 ./someModule.js
或 ./asyncSomeModule.js
.
另外一个可能会提出的问题是,我们已经使用了 await getSomeModule()
,但是在当前方案中,被调整为了 await getSomeModule
。如果我们采用这种解决方案,将会对现有代码造成很大的影响。
其实,只需要做一点点调整,就可以保持之前的文件命名和调用方式。
解决方案 B
// NodeJS 方式的 async 单例
const someModule = someAsyncOperationsToInitializeModule()
module.exports = () => someModule
现在,你无需改变任何外部代码。这种实现 100% 向后兼容。也就是说,你无需改造模块的调用方式,正如问题中所提到的调用方式一样。
// in async function
const getSomeModule = require('./getSomeModule')
const someModule = await getSomeModule()
Show me the Code
https://repl.it/@victoratas/Singleton-in-Nodejs