I'm making a function that will check if two files contain a given string. If both files DON'T contain the string, it should return undefined :(
here is my code:
var fs = require("fs");
function get_uniq(string, file1, file2, callback){
fs.readFile(file1, 'utf8', function(err, data1) {
if (err) throw err;
i = data1.search(string);
console.log(i);
if(i == -1){
fs.readFile(file2, 'utf8', function(err, data2) {
if (err) throw err;
j = data2.search(string);
if(j == -1){
return 1;
}
});
}
});
callback();
}
var i = get_uniq("stringThatFilesDoesntContainin", "somefile.txt", "anotherfile.txt", function(){
console.log(i);
});
Any idea what the problem is?
You should not rely on returning a computed value. In node functions can be executed asynchronously so it can return before the function can finish. To execute when the function completes a callback is given. For e.g.
fs.readFile(file1, 'utf8', function(err, data1) {...});
The function that is passed as the last argument is the callback. It is executed when the file has been read. Trying to return the data will result in undefined value.
In your case the returned values will be undefined for all cases. And callback will be executed in parallel with readFile.
callback must be called from inside readFile for file1 or file2, wherever it can finish logically. To give all the places where callback can be added are :
function get_uniq(string, file1, file2, callback){
fs.readFile(file1, 'utf8', function(err, data1) {
if (err)
{
throw err;
callback(err);
}
else
{
i = data1.search(string);
console.log(i);
if(i == -1){
fs.readFile(file2, 'utf8', function(err, data2) {
if (err)
{
throw err;
callback(err);
}
else
{
j = data2.search(string);
if(j == -1){
callback(false);
}
else
callback(true);
}
});
}
else
callback(false);
}
});
}
You can put you return value (true/false) as the argument to callback. Or catch error from inside it. How you will execute the above function will be like :
get_uniq("stringThatFilesDoesntContainin", "somefile.txt", "anotherfile.txt", function(value){
console.log(value);
});
Related
This function is a part of async.waterfall
The arr contains 2 objects with foldername, filename, width, height, etc.
I want to perform file operations on each of those files and put those 2 files in another array photoArr.
Then I want to pass that photoArr array to the next function in async.waterfall.
The issue is:
I am unable to reach the callback function, which is the 3rd argument to async.each. The console logs calling next but never logs oops error or here hi.
function(arr, image, callback) {
console.log("function3");
var photoArr = [];
async.each(arr, function(value, next) {
Jimp.read(`${photosDirectory}/${value["Folder"]}/${value["Photo"]}.jpg`, (err, photo) => {
if(err) next(err);
else {
photo.resize(value["Size-X(cm)"] * 37.8, value["Size Y(cm)"] * 37.8).rotate(-90);
photoArr.push(photo);
if(photoArr.length == 2) {
console.log('calling next');
next(null);
}
}
});
}, function(err) {
if(err) {
console.log('oops error');
console.log(err);
} else {
console.log("here hi");
console.log(photoArr.length);
callback(err, arr, image, photoArr);
}
});
}
I am new to nodejs and trying to cat multiple css files on-the-fly while coding. The package chokidar allow me to call a function when a file is modified, however I have a problem with the execution.
var goconcat =
fs.readdir(paths, function (err, files) {
if (err) {console.log(err);}
fs.unlink(paths + 'concat.css', function (err) {
if (err) throw err;
var list = files.map(function (files) {
return path.join(paths, files);
});
concat(list, paths + 'concat.css', function(err) {
if (err) throw err
});
});
});
I want to first delete the previous file, then read the directory and then write a new "concat.css". However I have an error;
Error: ENOENT: no such file or directory, open 'public/css/concat.css'
at error (native)
It appears that the function concat() is executed before the directory update and not after, and therefore it is trying to cat a file that just have been deleted. Why ?
I know that nodejs is executing functions in a synchronous way but I can't find a way to solve this problem. I tried async but I can't declare a variable between two functions and I couldn't manage to make it work.
If it cannot exist in a callback, using the setTimeout(fn, 0) trick may help make sure it's executed after the variable assignment.
var goconcat =
fs.readdir(paths, function (err, files) {
if (err) {console.log(err);}
fs.unlink(paths + 'concat.css', function (err) {
if (err) throw err;
var list = files.map(function (files) {
return path.join(paths, files);
});
setTimeout(function() {
concat(list, paths + 'concat.css', function(err) {
if (err) throw err
})}, 0);
});
});
The problem you're having is that your concat function is being invoked before the file is deleted by invoking unlink. You can prevent this by having nested callbacks; however, you can probably have better control flow if you use a module like async, and prevent yourself from dealing with Callback Hell.
Below is an example on how you can use the async module.
var fs = require('fs');
var async = require('async');
var myDir = __dirname + '/data';
async.waterfall([function(callback) {
fs.readdir(myDir, 'utf-8', function(error, files) {
if (error) {
return callback(error);
}
return callback(null, files);
});
}, function(files, callback) {
fs.open(myDir + '/myFile', 'wx', function(error, f) {
if (error && error.code === 'EEXIST') {
return callback(null, 'EEXIST');
}
return callback(null, 'CREATE');
});
}, function(fileStatus, callback) {
if (fileStatus === 'EEXIST') {
console.log('File exists. Deleting file...');
fs.unlink(myDir + '/myFile', function(error) {
if (error) {
return callback(error);
} else {
return callback(null);
}
});
} else {
console.log('File does not exist...');
return callback(null);
}
}, function(callback) {
fs.writeFile(myDir + '/myFile', "Hello World", function(err) {
if(err) {
return callback(error);
}
return callback(null, 'File Created');
});
}], function(error, results) {
console.error(error);
console.log(results);
});
The waterfall function runs the tasks array of functions in series,
each passing their results to the next in the array. However, if any
of the tasks pass an error to their own callback, the next function is
not executed, and the main callback is immediately called with the
error.
I'm wondering what the best way to handle errors in long functions with promises are?
My function:
module.exports = function(data) {
var deferred = Q.defer();
var config = {};
fs.readdir(path, function(err, files) {
if (err) {
deferred.reject(new Error(err));
}
var infoPath = false;
files.forEach(function(filename) {
if (filename.indexOf('.info') !== -1) {
infoPath = path + '/' + filename;
config['moduleName'] = filename.replace('.info', '');
}
});
if (!infoPath) {
deferred.reject(new Error('Did not find .info-file'));
}
if (files.indexOf(config['moduleName'] + '.module') > -1) {
config.type = 'Modules';
}
else {
deferred.reject(new Error('Unknown project type'));
}
// Parse the info file.
fs.readFile(infoPath, function (err, content) {
if (err) {
deferred.reject(new Error(err));
}
data.config = config;
data.infoContent = content.toString();
deferred.resolve(data);
});
});
return deferred.promise;
};
As far as I understand it this is the way to use Q.defer. But if a error is thrown, I don't want/need it to try the rest of function. Am I missing something or are there a better way to do this?
Rejecting a promise doesn't miraculously stop the function from executing the rest of its code. So after rejecting, you should return from the function:
if (err) {
deferred.reject(new Error(err));
return;
}
Or shorter:
if (err) {
return deferred.reject(new Error(err));
}
I wrote a function to copy a directory to another... But there's a problem : I use callback function to send the copied size. This callback comes too early (before the end of the copy). I think the problem is that the process is asynchronous. Can you help me?
var fs=require('fs');
var copyDir=function copyDir(from, to, callback){
if(!fs.existsSync(to)){
fs.mkdirSync(to);
}
console.log(from+" ==> "+to);
var count = 0;
fs.readdir(from, function(err,files){
for(var i=0;i<files.length;i++){
var f = from+"/"+files[i];
var d = f.replace(from, to);
console.log(f+" ("+i+")"+ " : "+d);
if(!fs.existsSync(d)){
if(!fs.statSync(f).isFile()){
//fs.mkdirSync(f.replace(from, to));
count += fs.statSync(f).size;
console.log(f + " will make an inception!")
copyDir(f, f.replace(from, to), function(err, cp){callback(err, cp)});
}else{
var size = fs.statSync(f).size;
copyFile(f, f.replace(from, to), function(err){
if(err) callback(err, count)
});
count += size;
callback(null, count);
}
}
}
});
}
function copyFile(source, target, cb) {
fs.readFile(source, function (err, data) {
if (err) throw err;
fs.writeFileSync(target, data, function (err, data){
if(err) throw err;
cb(null, fs.statSync(source).size); //This callback comes before the copy end.
});
});
}
exports.copyDir = copyDir;
copyDir is called by:
io.sockets.on('connection', function(socket){
console.log('connection');
socket.on('startCopy', function(data){
sizeDir('templates', function(e, r){
copyDir('templates', 'tmp', function(err, cp){
console.log("copy % " + Math.round(100*cp/r));
socket.emit('copy', {prog: Math.round(100*cp/r)});
});
});
});
});
You can rewrite your else code with following:
(function() {
var size = fs.statSync(f).size;
copyFile(f, f.replace(from, to), function(err){
if(err) {
callback(err, count);
return;
}
count += size;
callback(null, count);
});
})();
But, you have a lot of synchronous function in your code. You should know about all caveats of this approach. This article may be helpful
Assuming I have the following in a function:
exports.addnames = function(req, res) {
var names = ["Kelley", "Amy", "Mark"];
for(var i = 0; i < names.length; i++) {
(function (name_now) {
Person.findOne({ name: name_now},
function(err, doc) {
if(!err && !doc) {
var personDoc = new PersonDoc();
personDoc.name = name_now;
console.log(personDoc.name);
personDoc.save(function(err) {});
} else if(!err) {
console.log("Person is in the system");
} else {
console.log("ERROR: " + err);
}
}
);
)(names[i]);
}
My issue is after I save the names, I want to return the results:
Person.find({}, function(err, doc) {
res.json(200, doc);
})
Though I have a callback for names, it appears that the last block of code (Persons.find({})) gets executed before the calls to save all the names is complete... thusly when the user goes to the url in the browser, "doc" is empty... Is there some way I can ensure that the Persons.find({}) is called after the for loop completes?
The easiest way to do things like this is to use an async library like the aptly named async which can be found at https://github.com/caolan/async.
If you have a list of names that you want to save and then return when complete, it would look like:
// save each of the names asynchronously
async.forEach(names, function(name, done) {
Person.findOne({name: name},
function(err, doc) {
// return immediately if there was an error
if(err) return done(err);
// save the person if it doesn't already exist
if(!doc) {
var personDoc = new PersonDoc();
personDoc.name = name;
console.log(personDoc.name);
// the async call is complete after the save completes
return personDoc.save(done);
}
// or if the name is already there, just return successfully
console.log("Person is in the system");
done();
}
);
},
// this function is called after all of the names have been saved
// or as soon as an error occurs
function(err) {
if(err) return console.log('ERROR: ' + err);
Person.find({}, function(err, doc) {
res.json(200, doc);
})
});