0

I'm reading about requireing a JSON object from a file. I can read the JSON correctly, but after I add data to it and save it into the file, re-requiring doesn't get the updated JSON, but retrieves the old file. Can someone explain why this is so? How can I fix this?

var file = process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'] + "\\AppData\\Roaming\\BetterDiscord\\plugins\\Test.json";
var json = require(file);
console.log(JSON.stringify(json.img));
json.img.push({name:"Test"});
console.log(JSON.stringify(json.img));
save(JSON.stringify(json), file);
json = require(file);
console.log(JSON.stringify(json.img));

This is the save method:

var save = function(value, file){
    var fs = require('fs');
    fs.writeFile(file, value, function(err) {
        if (err) {
            console.log(err);
        }
    });
};

This is the Output:

Output 1: [{"name":"A"},{"name":"B"}]
Output 2: [{"name":"A"},{"name":"B"}, {"name":"Test"}]
Output 3: [{"name":"A"},{"name":"B"}]
pushkin
  • 9,575
  • 15
  • 51
  • 95
Letsplaybar
  • 72
  • 1
  • 13

2 Answers2

3

There's two major problems with your code. The first is that save is an asynchronous function. That means that any code you write directly after it will run before it completes. It works just like setTimeout in that regard.

console.log(1);
setTimeout(function() {
  console.log(2);
}, 100);
console.log(3);

Notice how it outputs 3 before 2. That's because setTimeout, like fs.writeFile, is asynchronous. To make sure you run code after it's done, you can pass a callback or use Promises (outside of the scope of this question but very useful). So your code could look something like this.

const save = (path, cb) => {
  setTimeout(() => {
    cb();
  }, 100);
};

let path = 'path/to/file';
save(path, () => {
  console.log(`Saved ${path}`);
});
console.log(`Saving ${path}...`);

This whole thing could also be avoided by using the sync version of writeFile.

fs.writeFileSync(file, value);

The next problem stems from how require caches the results. It does this because it's intended for loading in modules and they will not be changing dynamically, most of the time. Instead, load the file from the system directly. This can also be used asynchronously or synchronously. Here's how I would rewrite your code using synchronous code (although asynchronous is generally preferable).

var fs = require('fs');
var save = function(value, file){
    return fs.writeFileSync(file, value);
};

var file = process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'] + "\\AppData\\Roaming\\BetterDiscord\\plugins\\Test.json";
var obj = JSON.parse(fs.readFileSync(file, 'utf8'));
obj.img.push({name:"Test"});
console.log(JSON.stringify(obj.img));
save(JSON.stringify(obj), file);
obj = JSON.parse(fs.readFileSync(file, 'utf8'));
console.log(JSON.stringify(obj.img));
Mike Cluck
  • 31,869
  • 13
  • 80
  • 91
1

require will cache the file. Instead of requiring, use the fs.readFileSync method to read the file again.

json = fs.readFileSync(file);

Also, as @MikeC points out, you're writing to the file asynchronously, so either switch that out for the synchronous version writeFileSync, or rewrite your code using with readFile and writeFile and use callbacks.

pushkin
  • 9,575
  • 15
  • 51
  • 95