Cyber Sausage
Cyber Sausage

Reputation: 3

fs.readFileSync only sometimes returns a valid string in specific circumstances

In my node application, I have an init function:

function init() {
    if (!fs.existsSync("../schedule.json"))
        fs.createReadStream('../template.json').pipe(fs.createWriteStream('../schedule.json'));

    var content = fs.readFileSync("../schedule.json", "utf8");

    return JSON.parse(content);
}

Whenever I try to run it from cmd or git bash it crashes because of the error:

SyntaxError: Unexpected end of JSON input

So I debugged it in vscode and found out that the variable content only contained an empty string which means that readFileSync is returning an empty string.

Now here is the weird thing; it never works if started in cmd or git bash (neither in regular mode or debug) or when started in debug mode in vscode but if I restart the debugger in vscode (not starting and stopping but actually hitting the restart button) then I get the correct content of the json file as a string returned from readFileSync about every other time.

Update: I tried deleting the schedule.json file and then stepping through the code. It showed that it actually didn't enter the if(!fs.existsSync("../schedule.json")) statement. It's almost like a caching bug.

Upvotes: 0

Views: 1409

Answers (1)

James Thorpe
James Thorpe

Reputation: 32212

pipe causes things to happen asynchronously - it's surprising that it ever works inside vscode, unless perhaps you're stepping through allowing pipe time to finish its work, or perhaps when you use restart it's seeing the file from the previous run.

Instead, you need to wait for it to finish doing what it's doing:

var writer = fs.createWriteStream('../schedule.json');
fs.createReadStream('../template.json').pipe(writer);
writer.on('finish', function() {
  //It's safe to read the file here
});

However, that's not going to work for you, since your function is expecting to work entirely synchronously and return a result. You can make the above work if you switch to using callbacks or promises (which may or may not require a large amount of work, depending on what's going on around the call to init), or you can just use synchronous functions similar to what you're already using instead of pipe:

function init() {
    var templ = null;
    if (!fs.existsSync("../schedule.json")) {
        templ = fs.readFileSync("../template.json", "utf8");
        fs.writeFileSync("../schedule.json", templ, "utf8");
    }

    var content = templ != null ?
                  templ :
                  fs.readFileSync("../schedule.json", "utf8");

    return JSON.parse(content);
}

Upvotes: 2

Related Questions