Why does asynchronous writeFile method behave synchronously? - javascript

I have a simple JS file that reads a text file and the writes it and reads the changed file. For learning purposes, I have implemented the below code.
When the control reaches writeFile, shouldn't it be running in the background and the control should go to the console.log("Test") line and then back to the writeFile? But it actually fully executes the writeFile and control goes to the log line which is basically a synchronous behavior. Am I missing something here ?
console.log("Begin")
var fs = require("fs");
fs.readFile("input.txt", function(err, data) {
if (err) {
console.log(err);
} else {
console.log("Inside Read " + data.toString());
fs.writeFile("input.txt", "Replaced Text", function(err) {
if (err) {
console.log(err);
} else {
console.log("Inside Write");
var data = fs.readFileSync("Input.txt") console.log(data + " is the replaced text");
}
console.log("Test2");
});
}
});
console.log("Ended");

console.log("Test2") is inside of the writeFile callback function and will not run until writeFile is completed.
var fs = require("fs");
fs.readFile("input.txt", function(err, data) {
console.log("This will second, after print after readFile is complete);
fs.writeFile("input.txt", "Replaced Text", function(err) {
console.log("This will print last, after writeFile is complete");
});
console.log("This will print third, before writeFile is complete");
});
console.log("This will print first");

Related

appendFile() runs before readFile() even though appendFile() is chronologically after the readFile()

I am trying to write code that reads a file, counts the lines in it, and then adds another line with the line's number in the beginning. Like an index, basically. The problem is that the fs.appendFile() starts running before fs.readFile() is finished, but I am not sure as to why. Is there something I am doing wrong?
My code:
fs.readFile('list.txt', 'utf-8', (err, data) => {
if (err) throw err;
lines = data.split(/\r\n|\r|\n/).length - 1;
console.log("Im supposed to run first");
});
console.log("Im supposed to run second");
fs.appendFile('list.txt', '[' + lines + ']' + item + '\n', function(err) {
if (err) throw err;
console.log('List updated!');
fs.readFile('list.txt', 'utf-8', (err, data) => {
if (err) throw err;
// Converting Raw Buffer dto text
// data using tostring function.
message.channel.send('List was updated successfully! New list: \n' + data.toString());
console.log(data);
});
});
My output:
Im supposed to run second
List updated!
Im supposed to run first
[0]first item
Currently, you are using readFile and appendFile. Both of these functions are asynchronous and will run at the same time, returning whenever they complete.
If you'd like to run these synchronously, you can use the fs.readFileSync and fs.appendFileSync methods to synchronously read and append to the files.
Therefore, with something like the following:
const readFileData = fs.readFileSync("list.txt");
fs.appendFileSync('list.txt', '[' + lines + ']' + item + '\n');
The first line of code will run, then the second line of code.
The functions you are using are asynchronous, so the response of the second function can be received before the response of the first one.
fs.readFile('list.txt', 'utf-8', (err, data) => {
if (err) throw err;
lines = data.split(/\r\n|\r|\n/).length - 1;
console.log("Im supposed to run first");
appendFile(lines);
});
let appendFile = (lines)=> {
fs.appendFile('list.txt', '[' + lines + ']' + item + '\n', function(err) {
console.log("Im supposed to run second");
if (err) throw err;
console.log('List updated!');
fs.readFile('list.txt', 'utf-8', (err, data) => {
if (err) throw err;
// Converting Raw Buffer dto text
// data using tostring function.
message.channel.send('List was updated successfully! New list: \n' + data.toString());
console.log(data);
});
});
}

Returning data from js function not arriving in calling function

I am following the Electron filesystem tutorial here:
How to choose , read, save, delete or create a file with Electron Framework
Which includes the following code to pull up a load file dialog:
document.getElementById('select-file').addEventListener('click',function(){
dialog.showOpenDialog(function (fileNames) {
if(fileNames === undefined){
console.log("No file selected");
}else{
document.getElementById("actual-file").value = fileNames[0];
readFile(fileNames[0]);
// myData = readFile(fileNames[0]); <---
}
});
},false);
And the following code to actually read the file:
function readFile(filepath) {
fs.readFile(filepath, 'utf-8', function (err, data) {
if(err){
alert("An error ocurred reading the file :" + err.message);
return;
}
document.getElementById("content-editor").value = data;
});
}
As you can see, the last line updates the DOM with the imported data. However, I want to return the data to my calling function because it isn't HTML. Note the commented line towards the end of the first snipped. That's the kind of thing I want to do.
It's actually XML that has to be processed rather than dumped into the DOM. But all my attempts to return data (notably, 'return data;' either where the last line is or before the final curly brace) have failed. Neither results in the calling function getting the data.
Is it possible that the calling function needs to wait for this one to complete? Or, more likely, I'm just not returning the data correctly from this type of function. Any help is much appreciated.
Since your readFile(filepath) is performing the async file read operation you cannot use the simple return here. However you can pass a callback to your readFile function and call it later whenever your read operation is completed with the data. Like this.
Create a callback
function fileReadComplete(data) {
myData = data;
// Do whatever you want
}
and And call it as .
readFile(fileNames[0], fileReadComplete);
Change your readfile function:
function readFile(filepath, callback) {
fs.readFile(filepath, 'utf-8', function (err, data) {
if(err){
alert("An error ocurred reading the file :" + err.message);
return;
}
callback(data);
document.getElementById("content-editor").value = data;
});
}
As far as i understood, this should do what you want.
// Update; changed from "return" to callback function.
document.getElementById('select-file').addEventListener('click',function(){
dialog.showOpenDialog(function (fileNames) {
if(fileNames === undefined){
console.log("No file selected");
}else{
document.getElementById("actual-file").value = fileNames[0];
readFile(fileNames[0], function(myData){
console.log(myData);
});
}
});
},false);
function readFile(filepath, callBack) {
fs.readFile(filepath, 'utf-8', function (err, data) {
if(err){
alert("An error ocurred reading the file :" + err.message);
return;
}
callBack(data);
//document.getElementById("content-editor").value = data;
});
}

How to stop infinite loop caused by callback in asynchronous function

I have two async functions defined in my program; one of them pings a given IP address(using the net-ping module) and 'returns' (through a callback) whether the ping was successful, and the other creates an SSH connection to a given IP address (using the ssh2 module) and also 'returns' whether the connection was successful or not.
My problem arises when I try to read in IP data from a data.txt file using the readline functionality. I use readline module to read in each line from the file, use the callbacks to call ping and ssh in a blocking fashion, and then place these return values into an array to use later. I've been able to verify that the functions are executed in the order I expect, and that the return values are truly returned.. However, when the program reaches the end of the file - instead of terminating, it just reads the file again, and again, and again.
The SSH function is defined as:
function SSH(ipAdress, callback) {
connection.on('ready', function(err) {
if(err) {
throw err;
returnValue = "ErrorWithReadyConnection";
callback(null, returnValue);
}
//runs the uptime command once we have SSH'd into the machine
connection.exec('uptime', function(err, stream) {
if(err) {
throw err;
returnValue = "ErrorRunningCommandFromSSH";
callback(null, returnValue);
}
else {
stream.on('close', function(code, signal) {
//code holds the response from running 'uptime'
console.log("Stream on close. Code : " + code +
". Signal: " + signal);
connection.end();
returnValue = "success";
callback(null, returnValue);
}).on('data', function(data) {
console.log("STDOUT: " + data);
}).stderr.on('data', function(data) {
console.log("STDERR: " + data);
});
}
});
//Parameters for the SSH connection
}).connect({
host: ip,
port: 22,
username: userName,
privateKey: privateKey,
passphrase: passPhrase
}); //connect
//handle any connection errors done whilst creating a connection
connection.on('error', function(err) {
if(err) {
returnString = "ErrorWaitingForHandshake";
callback(null, returnString);
}
}); //end connect
}
The ping function is defined as:
function pingFunc(ip, callback) {
var returnString;
//Create a ping session, passing the IP of the machine to ping
sessions.pingHost(ip, function(error, target) {
//if error, then examine what type of error has occured
if(error) {
if(error instanceof ping.RequestTimedOutError) {
returnString = "RequestTimedOut";
}
//else, different error - output the error string.
else {
returnString = "Error";
}
} //End error handling
//else, ping was successful
else {
returnString = "Alive";
}
callback(null, returnString);
});
//return returnString;
}
And the code where I call the functions is:
var allMachines = new Array();
var lineReader = require('readline').createInterface({
input: require('fs').createReadStream('data.txt');
});
lineReader.on('line', function(line) {
pingFunc(line, function(err, pingResult) {
SSH(line, function(err, sshResult) {
var machineTemp = [{'ping': pingResult, 'ssh': sshResult }];
allMachines = allMachines.concat(machineTemp);
})
})
}
I plan on later using the allMachines array to create a JSON file, however this infinite loop is halting all potential progress. I've tried to move the connection.on('error', ...) in the SSH function which I think is causing the infinite loop, however this has proved to be fruitless.
Any help would be greatly appreciated - thanks!
P.S If anyone knows how I would be able to register when readlines has finished, and the allMachines array has been filled with the necessary data I would also be very grateful! I've tried to use readline.on('close', ...), however this gets called before SSH and ping finish executing, so is no use to me! Thanks again

better way of writing nodejs function of readfile/write file

How can i write this code in a better way.
var fs = require('fs');
var file = '/test.txt';
fs.readFile(file, 'utf8', function (err, txt) {
if (err) return console.log(err);
txt = txt + '\nAppended something!';
fs.writeFile(myFile, txt, function (err) {
if(err) return console.log(err);
console.log('Appended text!');
});
});
Suppose i have multiple callback then how can we prevent this callback of callback and so on....
getData(function(a){
getMoreData(a, function(b){
getMoreData(b, function(c){
getMoreData(c, function(d){
getMoreData(d, function(e){
...
});
});
});
});
});
I really like bluebird for this:
First you have to 'promisify' fs. n the example below they directly promisify the readFile method:
var readFile = Promise.promisify(require("fs").readFile);
readFile("myfile.js", "utf8").then(function(contents) {
return eval(contents);
}).then(function(result) {
console.log("The result of evaluating myfile.js", result);
}).catch(SyntaxError, function(e) {
console.log("File had syntax error", e);
//Catch any other error
}).catch(function(e) {
console.log("Error reading file", e);
});
or:
var fs = Promise.promisifyAll(require("fs"));
// note now you have to put 'async' after the methods like so:
fs.readFileAsync("myfile.js", "utf8").then(function(contents) {
console.log(contents);
}).catch(function(e) {
console.error(e.stack);
});
I suggest async waterfall
Your first snippet would look like following:
var txt;
async.waterfall([
function(callback) {
fs.readFile(file, 'utf8', callback);
},
function(txt, callback) {
txt = txt + '\nAppended something!';
fs.writeFile(myFile, txt, callback);
},
function(callback) {
console.log('Appended text!');
callback();
}
], function (err, result) {
console.log(err)
});
What you're describing is callback hell and there's a couple of smart ways to get around it. I don't claim to be the know it all but there's a whole website called callbackhell.com that you might want to check out.
But for the short answer, you can do these things
1. keep your code shallow, or name your functions
Instead of writing your fs.readFile with an anonymous function, name it and call it like so
fs.readFile(file, 'utf8', function readFileCb(err, txt) {
if (err) throw new Error(err);
txt = txt + '\nAppended something!';
fs.writeFile(myFile, txt, function (err) {
// no need to return a console.log, just throw Error should suffice
if(err) throw new Error(err);
console.log('Appended text!');
});
});
2. Modularize your code. Have named functions or libraries that do exactly one thing
function writeFile(file, txt, cb){
fs.writeFile(file, txt, cb)
}
function writeFileCb(err){
if(err) throw new Error(err);
console.log('Appended Text!');
}
fs.readFile(file, 'utf8', function readFileCb(err, txt) {
if (err) throw new Error(err);
txt = txt + '\nAppended something!';
writeFile(myFile, txt, writeFileCb);
});
3. Ensure that all errors are caught. You seem to be doing that well, so kudos!
You can also use Promises, libraries like Async waterfall, but callbacks are an essential parts of JavaScript and going through callback hell is just a matter of having good sense in writing your code.

Nodejs mongoose Mass/Batch update from file

So i have a csv file containing my information, i need to do a mass add/update
exports.add_questions_from_file = function (file_path, surveyid, callback)
{
var U = [{}];
fs.readFile(file_path, 'utf8', function(err, data){
if (err){
console.log(err);
callback(err,null);
}else{
console.log(data);
d = data.split(/\r\n|\n/);
for (x=0;x <d.length;x++)
{
line = d[x].split(',');
if (line[0] == "") {return};
RQuestion.add_by_line (line,function (err, question)
{
U.push({id:question.id});
console.log(U);
});
}
}
});
Survey.update({_id:surveyid},{$push:{"SurveyQuestions":U}},function (err,numAffected, rawResponse) {
console.log(rawResponse);
RET = {"module":"survey","operation": "add", "status":"OK"};
callback(RET);
});
};
But even though im using callback functions the update seems to happen with the same object always, even the console.log here
U.push({id:question.id});
console.log(U);
returns the same object (even that all the other were created)
Im doing something wrong?
I see a few issues.
First for:
if (line[0] == "") {return};
Don't you mean to use a break or continue instead? Otherwise the entire function will quit if there is a blank line anywhere in the file. This is very important because Survey.update won't get called either.
Second: I assumed that RQuestion.add_by_line and Survey.update are doing something async like updating a database. Your code needs to be restructured to wait for those async items to complete before moving on to the next step. I'd recommend an npm package named async for that.
fs.readFile(file_path, 'utf8', function(err, data){
if (err){
console.log(err);
callback(err,null);
}else{
d = data.split(/\r\n|\n/);
async.map(d, function(line, callback) {
//this function is called for each line
add_by_line (line,function (err, question)
{
callback(err,{id:question.id});
});
}, function(err, results) {
//this function is called when all of the items are done
console.log("done with async");
console.dir(results);
Survey.update({_id:surveyid},{$push:{"SurveyQuestions":results},function (err,numAffected, rawResponse) {
console.log(rawResponse);
RET = {"module":"survey","operation": "add", "status":"OK"};
callback(RET);
});
});
}
});

Categories

Resources