如何以 Node.js 方式編寫單例

wangxihua916發表於2020-06-19

文 / 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])。所以一旦一個 promiseresolved 並匯出,無論誰 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

repl.it/@victoratas/Singleton-in-N...

補充資料

[1] nodejs.org/api/modules.html#module...

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章