如何以 Node.js 方式编写单例

如何以 Node.js 方式编写单例

文 / Victor - AfterShipjavascript

译 / 吴天成 - AfterShiphtml

问题描述

如下面这种方式,写单例很容易:java

let someModule

async getSomeModule() {
    if (!someModule) {
        someModule = await someAsyncOperationsToInitializeModule()
    }
    return someModule
}

module.exports = getSomeModule
复制代码

一般以这种方式使用它:node

// in async function
const getSomeModule = require('./getSomeModule')
const someModule = await getSomeModule()
复制代码

除非你但愿将模块的加载延迟到初次运行时,不然不鼓励这种方式。api

由于,这将带来不少不必的分支代码(例如,if statement ),实际上咱们但愿避免这种代码。并且使用 let 语法将会中断静态代码分析,致使 IDE 不能正确推导出 someModule 的类型。promise

解决方案 A

请注意,node 的模块系统默认为单例(模块在第一次required的时候将会被缓存[1])。因此一旦一个 promiseresolved 并导出,不管谁 require(加载) 模块,它将始终返回第一次 resolved 的结果。缓存

如下是只使用const来实现的方式:async

// NodeJs 方式的 async 单例
// someAsyncOperationsToInitializeModule 为 async function
// 注意,此处执行函数,未 await
const someModule = someAsyncOperationsToInitializeModule()

module.exports = someModule
复制代码

2 行代码,就够了。函数

你该如何使用这个模块呢?ui

// 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

repl.it/@victoratas…

补充资料

[1] nodejs.org/api/modules…

相关文章
相关标签/搜索