I have fs.writeFileSync and fs.readFile operation for my app to work. I have put both these operations under promise. I process other statements only after these promises are resolved.
What I have observed is read is happening before write has completed
Here is my code snippet
To illustrate the above problem I have created a code snippet that writes random number to a file and later it will read file. If the file content is same as latest generated random number. Then read and write is synchronized.
file.js
let fs = require('fs');
let fileContent;
let randomNumbers = Math.ceil(Math.random() * 10);
function test (filepath) {
(async () => {
await writefile(filepath);
param = await readfile(filepath);
console.log(param);
}) ();
}
function writefile(filepath) {
let promise;
promise = new Promise((resolve,reject) => {
fs.appendFileSync(randomNumbers,filepath);
})
return promise;
}
function readfile(filepath) {
let promise;
promise = new Promise((resolve,reject) => {
fileContent = fs.readFileSync(filepath);
if( fileContent == randomNumbers ) {
console.log("file read and write is synchronized");
} else {
console.log("file read and write is not synchronized");
}
})
return promise;
}
I believe you're misusing your Promise objects.
await writeFile(path)
won't complete until you call the resolve() method in your Promise function.
Maybe you should change writeFile() function to look like this, and make a similar change to readFile().
function writefile(filepath) {
return new Promise((resolve,reject) => {
fs.appendFileSync(randomNumbers,filepath);
resolve()
})
}
But, it has to be said, the sync functions in fs get rid of the need for promises. So you could do this just as easily.
function test (filepath) {
fs.appendFileSync(randomNumbers,filepath);
fileContent = fs.readFileSync(filepath);
console.log(fileContent);
}
Or you could wrap the async functions in promises, like this:
function writefile(filepath) {
return new Promise((resolve,reject) => {
fs.appendFile(randomNumbers,filepath, (err, result) => {
if (err) reject(err);
else resolve(result);
});
})
}
That's called promisifying the function.
Related
I am trying to parse data from a .csv file, and save it to an array for later use.
I understand the concept of promises, but I have no idea what am I missing in my code that I cannot resolve the Promise and get the value (the string in the .csv file). It while I can view all the data inside the promise (.on('data')) from debugging mode, I just can't save it in order to use it later in my 'try&catch'.
const fs = require("fs");
const csv = require("csv-parser");
const { resolve } = require("path");
async function readCSV(filepath) {
return new Promise(async (resolve, reject) => {
await fs
.createReadStream(filepath)
.pipe(csv())
.on("data", (data) => {
results.push(data);
})
.on("error", (error) => reject(results))
.on("end", () => {
resolve(results);
});
});
}
const results = [];
const csvFilePath =
"/languages.csv";
try {
const languages = readCSV(csvFilePath).then((res) => {
return res;
});
console.log(languages);
} catch (e) {
console.log(e);
}
and the output on the console is:
>Promise {<pending>}
No debugger available, can not send 'variables'
** That's from the debugging mode when I pause inside the promise:
https://i.stack.imgur.com/H9nHi.png
You can't try catch a returned promise without the await keyword in an async function.
If you're returning a promise, you need to use the .catch method on the promise.
Also, when you're logging languages you're doing so before the promise resolves because you're not using the await keyword.
I'm sure the promise resolves. Instead, log res inside the .then method.
const fs = require("fs");
const csv = require("csv-parser");
const results = [];
function readCSV(filepath) {
return new Promise((resolve, reject) => {
fs
.createReadStream(filepath)
.pipe(csv())
.on("data", (data) => {
results.push(data);
})
.on("error", (error) => reject(results))
.on("end", () => {
resolve(results);
});
});
}
const csvFilePath = "./languages.csv";
(async () => {
const output = await readCSV(csvFilePath);
console.log(output)
})();
I have a node.js script that reads in some JS code from a file, and passes it to an eval(). The code that passes the javascript to the eval is as follows:
// read javascript code from file
var outputbuffer = '';
function output(data) {
outputbuffer += data + '\n';
}
eval(javascriptCodeFromFile);
// do stuff with outputbuffer
The javascript code in the file is as follows:
var fs = require('fs');
fs.readFile('myfile.txt', function(err, data) {
if (err) {
output('Error reading file');
}
else {
output(data.toString());
}
});
The problem is that the eval exits before the file read completes, as the file read is asyncronous. The eval simply starts the file read, and then exits, not waiting from the file read to complete. How can I make the eval wait for the callback before exiting? I have looked at promises and they seem PROMISing (pun intended) but I need someone to guide me in the right direction.
Leaving all the downsides of eval, here is a direct answer to your question.
Create a promise for keeping the event loop of the main thread busy.
Resolve the promise from within the script being eval. The script will have access to the resolve and reject` functions of the Promise since the evaluating script is within the scope of the promise.
let p = new Promise((resolve, reject) => eval(javascriptCodeFromFile));
p.then(()=> console.log(outputbuffer))
.catch((e) => console.log('Rejected ::' + outputBuffer));
Here is a runnable example that demonstrates the main thread waiting for an asynchronous execution happens inside eval
let jsCode = `
setTimeout(() => (resolve('Hello from eval after two seconds')), 2000);
`;
console.log('waiting...');
let p = new Promise((resolve, reject) => {
eval(jsCode);
})
p.then((data)=> console.log(data));
It's fine to have your eval'd code return a promise.
example:
const code = `
new Promise((resolve, reject) => {
var fs = require('fs');
fs.readFile('myfile.txt', function (err, data) {
if (err) {
reject('Error reading file');
}
else {
resolve(data.toString());
}
});
});
`;
console.log('starting...');
eval(code)
.then(str => console.log(str))
.catch(err => console.error(err));
It's also fine to make the Promise external to the eval and just call the resolve or reject from the eval'd code:
e.g.:
const code = `
var fs = require('fs');
fs.readFile('myfile.txt', function (err, data) {
if (err) {
reject(err);
}
else {
resolve(data.toString());
}
});
`;
console.log('starting...');
new Promise((resolve, reject) => eval(code))
.then(str => console.log(str))
.catch(err => console.error(err));
Or the same thing with await and calling the async version of readFile
const code = `
require('fs').promises.readFile('myfile.txt')
`;
console.log('starting...');
main().then(() => console.log('...done'));
async function main() {
try {
let result = await eval(code);
console.log(result);
} catch(err) {
console.error(err);
}
}
I am using a package called Okrabyte to extract words from each image file in a folder. The result should be a new array containing the extracted text that I can use in other functions.
When I run this:
var fs = require("fs");
var okrabyte = require("okrabyte");
fs.readdir("imgs/", function(err, files){
files.map((file)=>{
okrabyte.decodeBuffer(fs.readFileSync("imgs/"+ file), (err, data)=>{
let splitWords = data.split(" ");
let word = splitWords[0].substr(1);
console.log(word);
})
})
})
the console logs each word. To return an array with those words I've tried the following:
async function words() {
await fs.readdir("imgs/", function (err, files) {
return files.map(async (file) => {
await okrabyte.decodeBuffer(fs.readFileSync("imgs/" + file), async (err, data) => {
let splitWords = data.split(" ");
let word = splitWords[0].substr(1);
return word
})
})
})
}
var testing = await words();
console.log(testing);
This gives undefined I've tried turning everything into a promise, I've tried async-await, I've tried pushing each word into a new array and returning that array in closure but nothing works - what am I doing wrong??
If your map function is async then it's returning a promise, so your mapped array is in fact an array of promises. But you can then use a Promise.all to get the resolved values of that array.
Additionally, you're trying to await the call to fs.readdir and okrabyte.decodeBuffer, which both accept a callback and do not return a promise. So if you want to use a promise there you'll have to wrap them in a promise constructor manually.
Here's how I would do it:
async function words() {
// Wrap `fs` call into a promise, so we can await it:
const files = await new Promise((resolve, reject) => {
fs.readdir("imgs/", (err, files) => { err ? reject(err) : resolve(files); });
});
// Since map function returns a promise, we wrap into a Promise.all:
const mapped = await Promise.all(files.map((file) => {
// Wrap okrabyte.decodeBuffer into promise, and return it:
return new Promise((resolve, reject) => {
okrabyte.decodeBuffer(fs.readFileSync("imgs/" + file), (err, data) => {
if (err) return reject(err);
const splitWords = data.split(" ");
const word = splitWords[0].substr(1);
resolve(word);
})
})
}))
// Mapped is now an array containing each "word".
return mapped;
}
var testing = await words();
// Should now log your array of words correctly.
console.log(testing);
You should not be using async-await that way. That should be used when you are dealing with promises. The library okrabyte uses the concept of callbacks.
I suggest you follow this approach:
(1) Enclose the okrabyte.decodeBuffer part in a function that returns a promise that resolves in the callback.
(2) Use files.map to generate an array of promises calling the function you defined in (1)
(3) Use Promise.all to wait for all promises to execute and finish before moving on to dealing with all the words.
Walkthrough:
Part 1
const processWord = (file) => {
return new Promise((resolve, reject) => {
okrabyte.decodeBuffer(fs.readFileSync("imgs/"+ file), (err, data)=>{
if (err) {
reject(err); // <--- reject the promise if there was an error
return;
}
let splitWords = data.split(" ");
let word = splitWords[0].substr(1);
resolve(word); // <--- resolve the promise with the word
})
});
}
You make a function that wraps the decoding part into a promise that eventually resolves with the word (or is rejected with an error).
Part 2
const promises = files.map((file)=>{
return processWord(file);
})
The above will generate an array of promises.
Part 3
fs.readdir("imgs/", function(err, files){
const promises = files.map((file)=>{
return processWord(file);
})
Promise.all(promises)
.then(responses => {
// responses holds a list of words
// You will get each word accessing responses[0], responses[1], responses[2], ...
console.log(responses);
})
.catch(error => {
console.log(error); // Deal with the error in some way
});
})
The above uses Promise.all to wait for all promises to resolve before going to the then() block, assuming no errors occurred.
You can further isolate the construct above in a method that will return a promise with a list of all the words, much in the same fashion that was done in the processWord function from Part 1. That way, you can finally use async-await if you wish, instead of handling things in the then() block:
const processEverything = () => {
return new Promise((resolve, reject) => {
fs.readdir("imgs/", function(err, files){
const promises = files.map((file)=>{
return processWord(file);
})
Promise.all(promises)
.then(responses => {
resolve(responses);
})
.catch(error => {
reject(error);
});
})
});
};
const words = await processEverything();
console.log(words);
You're returning word value to the enclosed function but not map() function.
Hope this code help you.
async function words() {
global.words = [];
await fs.readdir("imgs/", function(err, files){
return files.map( async(file)=>{
await okrabyte.decodeBuffer(fs.readFileSync("imgs/"+ file), async (err, data)=>{
let splitWords = data.split(" ");
let word = splitWords[0].substr(1);
global.words.push(word);
})
})
})
}
var testing = await words();
testing = global.words;
console.log(testing);
I have the following async function:
async function readFile () {
let content = await new Promise((resolve, reject) => {
fs.readFile('./file.txt', function (err, content) {
if (err) {
return reject(err)
}
resolve(content)
})
})
console.log(content)
}
readFile()
This runs just fine. It outputs the file buffer to the console as expected. But now, if I try to instead return the value:
async function readFile () {
let content = await new Promise((resolve, reject) => {
fs.readFile('./file.txt', function (err, content) {
if (err) {
return reject(err)
}
resolve(content)
})
})
return content
}
console.log(readFile())
I now get:
Promise { <pending> }
Why is this? Why can you use a value inside that function but when you return it out of the function it's now a Promise?
How do you actually make use of this in a normal workflow? For example, lets say I wanted to check if a file exists, then read in the file, then update some database with the content, the synchronous pseudo code would look something like this:
if (fileExists(path)) {
buffer = readFile(path)
updateDatabase(buffer)
}
That workflow consists of 3 individual async operations. How would you do something like this with async/await? Is the key that you have to have your entire script wrapped in an async function?
async function doSomething () {
if (fileExists(path)) {
buffer = readFile(path)
updateDatabase(buffer)
}
}
(Keep in mind that is just pseudo-code but hopefully its gets my point across).
All async functions return a promise as was mentioned in the comments. You could therefore re-write your readFile function like this:
function readFile() {
return new Promise((resolve, reject) => {
fs.readFile('./file.txt', function (err, content) {
if (err) {
return reject(err)
}
resolve(content)
})
})
}
You would then consume the return value of readFile via await:
console.log(await readFile()) // will log your actual file contents.
The usual workflow with this paradigm is to break your async operations into separate functions that each return a promise, and then run them all inside a broader async function, much like you suggest, but with awaits and some error handling like so:
async function doSomething () {
try {
const fileCheck = await fileExists(path)
if (fileCheck) {
const buffer = await readFile(path)
await updateDatabase(buffer)
// Whatever else you want to do
}
} catch (err) {
// handle any rejected Promises here.
}
}
const serchContentXmlFile = async (path, content) => {
return new Promise((resolve, reject) => {
fs.readFile(path, function (err, data) {
if (err) {
return reject(err)
}
resolve(data.indexOf(content))
})
})
}
await serchContentXmlFile("category.xml",xmlUrl);
How to use Typescript async / await function and return typescript default promises in node js FS module and call other function upon promise resolved.
Following is the code :
if (value) {
tempValue = value;
fs.writeFile(FILE_TOKEN, value, WriteTokenFileResult);
}
function WriteTokenFileResult(err: any, data: any) {
if (err) {
console.log(err);
return false;
}
TOKEN = tempValue;
ReadGist(); // other FS read File call
};
Since NodeJS 10.0.0 fsPromises module can be used to achieve this result.
import { promises as fsPromises } from 'fs';
await fsPromises.writeFile('file.txt', 'data')
For now I think there is no other way as to go with wrapper function. Something like this:
function WriteFile(fileName, data): Promise<void>
{
return new Promise<void>((resolve, reject) =>
{
fs.writeFile(fileName, data, (err) =>
{
if (err)
{
reject(err);
}
else
{
resolve();
}
});
});
}
async function Sample()
{
await WriteFile("someFile.txt", "someData");
console.log("WriteFile is finished");
}
There is some lengthy discussion here about promises in node.js: Every async function returns Promise
If you don't want to write the Promise wrappers yourself you can use async-file.
Using this your code could look something like the following...
(async function () {
//...
await fs.writeFile(FILE_TOKEN, value);
var data = await fs.readFile('gist');
// do something with your "gist" data here...
})();