Skip to content

Instantly share code, notes, and snippets.

@rokobuljan
Created November 20, 2023 02:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rokobuljan/cbc7d2f4071a4be1ae1a054379710a67 to your computer and use it in GitHub Desktop.
Save rokobuljan/cbc7d2f4071a4be1ae1a054379710a67 to your computer and use it in GitHub Desktop.
Concurrently (simultaneously) update JSON files via queue mechanism. [nodejs] [js] [json]
import fs from "fs-extra";
/**
* Concurrently (simultaneously) update JSON files via queue
* @example
* ```js
* import { QSON } from "./qson.js";
* const qconf = new QSON("./config.json");
*
* // Clear current file's data
* await qconf.write();
*
* await qconf.enqueue({foo: "bar"});
* await qconf.enqueue({baz: "tar"});
*
* const writes = [];
* for (let i = 1; i <= 10; ++i) {
* writes.push(qconf.enqueue({ [i]: Date.now() }));
* }
*
* await Promise.all(writes);
* console.log(await qconf.read());
* ```
* @author https://github.com/rokobuljan
*/
export class QSON {
static queues = {};
/**
* @param {string} filePath Path to .json file
*/
constructor(filePath = "", options) {
this.filePath = filePath;
this.queue = QSON.queues[this.filePath] ??= [];
Object.assign(
this,
{
retries: 5
},
options
);
}
/**
* @param {object} data Data to insert
* @returns {Promise<object>} The inserted data
*/
async enqueue(data = {}) {
// Always push new data to queue
this.queue.push(data);
// Start queue loop only when there's one entry record
if (this.queue.length === 1) {
await this.processQueue();
}
return Promise.resolve(data);
}
/**
* @return {Promise<object>} .json file's data
*/
async read() {
await fs.access(this.filePath, fs.constants.R_OK);
//`await` prevents "Unexpected end of JSON input"
return await fs.readJson(this.filePath);
}
/**
* @param {object} data Data to insert
* @return {Promise} on file write
*/
async write(data = {}) {
await fs.access(this.filePath, fs.constants.W_OK);
//`await` prevents "Unexpected end of JSON input"
return await fs.writeJson(this.filePath, data, { spaces: 2 });
}
/**
* Start processing the queue
* @return {Promise} on process end
*/
async processQueue() {
let retries = 0;
while (this.queue.length > 0 && retries <= this.retries) {
try {
const existingData = await this.read();
const fifoData = this.queue[0];
await this.write({ ...existingData, ...fifoData });
this.queue.shift();
} catch {
retries += 1;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment