文 / 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
repl.it/@victoratas/Singleton-in-N...
補充資料
[1] nodejs.org/api/modules.html#module...
本作品採用《CC 協議》,轉載必須註明作者和本文連結