I have this code:
const fs = require("fs");
const saveFile = (fileName, data) => {
return new Promise((resolve) => {
fs.writeFile(fileName, data, (err) => {
resolve(true);
});
});
};
const readFile = (fileName) => {
return new Promise((resolve) => {
fs.readFile(fileName, "utf8", (err, data) => {
resolve(data);
});
});
};
const filename = "test.txt";
saveFile(filename, "first");
readFile(filename).then((contents) => {
saveFile(filename, contents " second");
});
readFile(filename).then((contents) => {
saveFile(filename, contents " third");
});
I'm hoping to obtain in 'test.txt'
first second third
but instead, I get
first thirdd
The idea is that every time I receive a certain post request. I have to add more text to the file
Does someone have any solution for this?
Thank you so much!
CodePudding user response:
Because these are async functions they notify you that the work is completed in the then
function.
That means you want to use a then chain (or an async function) like so:
readFile(filename).then((contents) => {
return saveFile(filename, contents " second");
}).then(() => {
return readFile(filename)
}).then((contents) => {
saveFile(filename, contents " third");
});
CodePudding user response:
To anyone wondering what's happening here, this is because of asynchronicity in JavaScript. The following code:
saveFile(filename, "first");
readFile(filename).then((contents) => {
saveFile(filename, contents " second");
});
readFile(filename).then((contents) => {
saveFile(filename, contents " third");
});
...is essentially interpreted by the interpreter like this:
saveFile(filename, "first")
readFile(filename)
readFile(filename)
This is because these calls happen sequentially and start asynchronous contexts called promises. So, the file gets saved and then is read twice right away. Two promises are made and they each execute in the order that they were created. So the first promise gets executed, and then the second promise gets executed:
(contents => {
saveFile(filename, contents " second");
})()
(contents => {
saveFile(filename, contents " third");
})()
The issue here is that contents
is identical because readFile
was invoked twice right after saving.
By chaining the promises together you can avoid this altogether:
saveFile(filename, "first")
.then(() => readFile(filename))
.then(contents => saveFile(filename, contents " second"))
.then(() => readFile(filename))
.then(contents => saveFile(filename, contents " third"))
Or you can use async/await
, which is even easier to read:
async function start() {
let contents
await saveFile(filename, "first")
contents = await readFile(filename)
await saveFile(filename, contents " second")
contents = await readFile(filename)
await saveFile(filename, contents " third")
}
start()
I'll add one last refactor to all of your code, because I'm bored.
import { promisify } from "util";
import * as fs from "fs";
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
const filename = "test.txt";
async function appendFile(str) {
let contents = "";
if (fs.existsSync(filename)) contents = await readFile(filename, "utf8");
await writeFile(filename, `${contents} ${str}`);
}
["first", "second", "third"].reduce(async (promise, contents) => {
await promise;
return await appendFile(contents);
}, Promise.resolve());