我正在 node.js 中制作一個 Discord 機器人,并且需要存盤一些每個公會的資料。我想data/<guild.id>.json使用內置的將它存盤在單獨的 JSON 檔案中fs/promises。
我有一個setGuildData(guildID: string, dataToAssign: object)讀取當前 JSON 檔案的函式,將 JSON 決議為fileData,然后分配新資料,Object.assign()用于最終對 JSON 進行字串化并覆寫檔案。
但我擔心如果兩個異步函式呼叫嘗試同時編輯這個 JSON 檔案,它們會干擾:
file: {a:0, b:1}
call 1: read the file
call 2: read the file
call 1: edit the object to insert {c:2}
call 2: edit the object to insert {d:3}
call 1: stringify and write {a:0, b:1, c:2}
call 2 stringify and write {a:0, b:1, d:3}
file: {a:0, b:1, d:3}
我想要發生的是,當 call1 讀取 JSON 時,它會阻止對該檔案的任何其他讀取呼叫,直到它完成。這將導致呼叫 2 讀取{a:0, b:1, c:2}并添加{d:3}到它,從而給出所需的結果{a:0, b:1, c:2, d:3}
所以我的問題是:
我如何打開一個檔案,以便我可以讀取它,處理資料,然后覆寫它,同時阻止其他異步呼叫同時打開檔案?
阻止這個檔案長達一百毫秒的性能影響還不錯,因為這個檔案僅適用于那個公會,我希望在高峰時間每個公會每分鐘只看到幾個請求,很少同時出現兩個要求。我只是想安全起見,不要像{c:2}示例中發生的那樣意外洗掉任何資料。
我可以在 JS 中創建一個全域佇列來防止雙重檔案寫入,但我寧愿有一種作業系統級別的方法來臨時阻止這個檔案。如果這是特定于作業系統的,我使用的是 Linux。
這是我現在擁有的:
const fsPromises = require('node:fs/promises');
const path = require('node:path')
const dataPath = path.join(__dirname, 'data');
async function setGuildData(guildID, newData) {
let fileHandle = await fsPromises.open(path.join(dataPath, `${guildID}.json`), "w "); // open the file. note 1
let fileData; // make a variable for the file data
try {
fileData = JSON.parse(await fileHandle.readFile()); // try to read and parse the file.
} catch (err) {
fileData = {}; // if that doesn't work, assume the file was not made yet and return an empty object
// yes I tried logging the error, see note 1
}
await fileHandle.write(JSON.stringify(Object.assign(fileData, newData)), 0); // use Object.assign to overwrite some properties or create new ones, and write this to the file
await fileHandle.close(); // close the filehandle.
}
注意1:該"w "模式似乎在打開時清除檔案。我想要一種允許我先讀取然后覆寫檔案的模式,同時保持檔案打開以防止其他異步呼叫干擾。
uj5u.com熱心網友回復:
你可以使用 Promises 來做一個原子守衛。
下面是一個示例,如果不使用 makeAtomic,答案將是 1,而不是 2。
const sleep = ms => new Promise(r => setTimeout(r, ms));
function makeAtomic() {
let prom = Promise.resolve(null);
function run(cb) {
const oProm = prom;
prom = (async () => {
try {
await oProm;
} finally {
return cb();
}
})();
return prom;
};
return run;
}
const atom = makeAtomic();
let v = 0;
async function addOne() {
return atom(async c => {
let ov = v;
await sleep(200);
v = ov 1;
});
}
async function test() {
await Promise.all([addOne(), addOne()]);
console.log(v);
}
test().catch(e => console.error(e));
注意:您基本上可以與代碼的其他部分共享原子承諾,如果序列化很重要,它不必是相同的代碼,方便您想要同時執行多個操作的地方,當然您可以有多個 makeAtomics,這樣做使得并行操作非常簡單,當然也避免了異步編碼中容易發生的競爭條件。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/487426.html
標籤:javascript 节点.js linux 文件系统 锁定
上一篇:在滿足第一個if/else條件之前,如何阻止第二個提示出現?(游戲的起始頁)
下一篇:如何從此字串中洗掉前5個字符?
