Async.js Parallel Callback not executing - node.js

I'm working with the parallel function in Async.js and for some reason the final call back is not getting executed and I do not see an error happening anywhere.
I'm dynamically creating an array of functions that are passed to the parallel call as such:
// 'theFiles' is an array of files I'm working with in a code-generator style type of scenario
var callItems = [];
theFiles.forEach(function(currentFile) {
var genFileFunc = generateFileFunc(destDir + "/" + currentFile, packageName, appName);
callItems.push(genFileFunc(function(err, results) {
if(err) {
console.error("*** ERROR ***" + err);
} else {
console.log("Done: " + results);
}
}));
});
async.parallel(callItems, function(err, results) {
console.log(err);
console.log(results);
if(err) {
console.error("**** ERROR ****");
} else {
console.log("***** ALL ITEMS HAVE BEEN CALLED WITHOUT ERROR ****");
}
});
Then in an outside function (outside of the function that is executing the forEach above) I have the generateFileFunc() function.
// Function that returns a function that works with a file (modifies it/etc).
function generateFileFunc(file, packageName, appName) {
return function(callback) {
generateFile(file, packageName, appName, callback);
}
}
I've looked at this SO post and it helped me get to where I'm at. However the final call back is not being executed. All of the items in the parallel call are being executed though. Inside of gnerateFile (function) at the very bottom I call the callback, so thats golden.
Anyone have any idea why this might not be executing properly?
The end result is to work with each function call in parallel and then be notified when I'm done so I can continue executing some other instructions.
Thanks!

Analyze what is happening line by line, starting with this:
var genFileFunc = generateFileFunc(...);
Since your function generateFileFunc returns function, so variable genFileFunc is a following function
genFileFunc === function(callback) {
generateFile( ... );
};
Now it is clear that this function returns nothing ( there is no return statement ). And obviously by nothing I understand JavaScript's built-in undefined constant. In particular you have
genFileFunc(function(err, results) { ... } ) === undefined
which is the result of calling it. Therefore you push undefined to callItems. No wonder it does not work.
It is hard to tell how to fix this without knowing what generateFile exactly does, but I'll try it anyway. Try simply doing this:
callItems.push(genFileFunc);
because you have to push function to callItems, not the result of the function, which is undefined.

Curious.
Best guess so far: Inside generateFile, RETURN callback instead of calling it.

You can achieve the stated goal with
async.map(theFiles, function(file, done) {
generateFile(destDir + "/" + file, packageName, appName, done);
}, function(err, res) {
// do something with the error/results
});

Related

Model.findOne() function not working when passed a variable as the value

I am new to node.js.
I am trying to create function, where a randomly generated String is queried to check if it exists or not. If it already exists, the String is randomly generated till it is unique.
let validID = false;
console.log(temp); //temp is the randomly generated String.
while(!validID){
Website.findOne({shortcut: temp},function(err,docs){
if(docs==null){
validID = true;
console.log("The shortcut for the url is" + temp);
}else{
console.log("FOUND");
temp = generateId();
}
});
}
When run, the code is stuck in an infinite while loop.
I tried to see whether the code works with a String value ( not a variable ) passed in as the query inside findOne(). It worked. I am assuming that the fact that temp is a variable is causing the problem. Can variables be passed in as a value in a query? If so, what is the correct way?
Website.findOne operates asynchronously, i.e. the callback-function you passed to it, will be run once the results from the mongodb are fetched. However, node will not be able to actually process this callback, since your callstack never gets emptied due to your while-loop. If you're interested, you can find out more about this here.
One way to solve this is to wrap your Mongo-DB call in a promise, wait for it to resolve, then return if the ID is unique and continue by calling it recursively otherwise (note that this can be highly simplified by using async/await but for understanding how this works using promised are beneficial imo):
function findIdPromise(temp) {
return new Promise((resolve, reject) => {
Website.findOne({
shortcut: temp
}, function (err, docs) {
if (err) {
return reject(err);
}
resolve(docs);
});
});
}
function getNextIsUniqueIdPromise(shortcut) {
return findIdPromise()
.then(docs => {
if (docs == null) {
return shortcut;
}
return getNextIsUniqueIdPromise(generateId());
});
}
// call it initially with
getNextIsUniqueIdPromise(firstShortcutToCheck)
.then(shortcut => {
console.log("The shortcut for the url is" + shortcut):
})
.catch(err => {
console.log("an error occured", err):
});

Avoid callback multi-invocation when forEach is used

I have a function that processes an array of data (first parameter) and, once the procesing is finished, it invokes only one time a callback function (second parameter). I'm using forEach to process data item by item, consisting the processing of each item in some checkings and storing the param in database. The function storeInDB() does the storing work and uses a callback (second parameter) when the item has been stored.
A first approach to the code is the following:
function doWork(data, callback) {
data.forEach(function (item) {
// Do some check on item
...
storeInDB(item, function(err) {
// check error etc.
...
callback();
});
});
}
However, it's wrong, as the the callback function will be invoked several times (as many as element in the data array).
I'd like to know how to refactor my code in order to achieve the desired behaviour, i.e. only one invocation to callback once the storing work is finished. I guess that async could help in this task, but I haven't find the right pattern yet to combine async + forEach.
Any help is appreciated!
You can use a library such as async to do this, although I would recommend using promises if possible. For your immediate problem you can use a counter to determine how many storage calls have completed and call the callback when the total number are completed.
let counter = 0;
data.forEach(function (item) {
// Do some check on item
...
storeInDB(item, function(err) {
// check error etc.
counter++
if (counter == data.length) {
callback();
}
});
});
you can also utilize the three parameters passed to the function to execute on each array method
function doWork(data, callback) {
data.forEach(function (value,idx,arr) {
// Do some check on item
...
storeInDB(arr[idx], function(err) {
// check error etc.
...
if ( (idx + 1) === arr.length ) {
callback();
}
});
});
}
If storeInDB function returns a promise, you can push all async functions to an array and use Promise.all. After all tasks run successfully, It will invokes callback function.
Hope this helps you.
function doWork(data, callback) {
let arr = [];
data.map(function(itm) {
// Do some check on item
...
arr.push(storeInDB(item));
});
Promise.all(arr)
.then(function(res) {
callback();
});
}

what is function (err, results) { in the below?

I am starting with nodejs and read basics on callback, here i have a code,
exports.handler = (event, context, callback) => {
var bx = require('barcode-js');
// Set default values.
var params = {
input: './1.bmp',
type: 'qrcode'
};
bx.analyze(params, function (err, results) {
if (err) {
callback('There was an error processing this image: ' + err)
}
results.forEach(function(result) {
callback(null,'Result type :' + result.type + '\t'+'Value: '+result.value);
});
});
};
what is happening bx.analyze(params, function (err, results) { in this line. why can't we just use bx.analyze(params) ?
The second parameter is function. Called a callback.
When you execute an async function, you can't wait for its returned value:
var result = bx.analyze(params); //you can't do this
So you tell the function that when it finishes its job, just call the callback (the function you passed as second parameter).
//so `analyze()` will call your passed function rather than `return` the result
//`err` will contain the error object if anything wrong happened in the analyze() function
//`results` will contain result if everything was fine
bx.analyze(params, function (err, results) {
if (err)
callback('There was an error processing this image: ' + err)
});
I strongly recommend you to learn how async code works in javascript. You can't learn Nodejs untill then..
Update:
In your code, the function handler is an async function so it takes a callback as a parameter (Just like analyze()).
When this function done with its job, it calls the callback.
Now in your code, it is called 2 times:
1) When an error occured, this function will call the callback and pass the error into it:
if (err) {
callback('There was an error processing this image: ' + err); //its passing "err" returned from analyze()
}
2) When everything goes well, it passes result set:
callback(null, 'Result type :' + result.type + '\t'+'Value: '+result.value); //the 1st parameter is being passed as "null" because there is no error in it, and second parameter passes the result
This is callback function which make the code asynchronous.
Means you are passing a function as argument to your bx.analyze(params) method. This callback method is executed once your bx.analyze(params) is finished, so its not blocking the other code, making the code asynchronous.
If you need how this call back is executed , then you have to look for Event loop, search it in Google,have lots of documentation.
if we use bx.analyze(params) then it is blocking (synchronous) code. event loop stop until bx.analyze(params) did not return any value and
there is no error handling.
if we use bx.analyze(params, function (err, results) { }); then it is asynchronous (non-blocking) code. event loop will not wait for returning value it goes to next statement and when bx.analyze(params, function (err, results) { return the callback event loop process it.
There is error handlation is also done
for deep understanding see this video https://www.youtube.com/watch?v=8aGhZQkoFbQ

How to access a variable outside a function

Please bear with me on this. I can't seem to get my head round how I will be able to update the variable outside the FindOne function.
So:
var userPostCount;
userPostsModel.findOne({'profileID':req.session.id}, function(err, usersPostCountDB) {
if(usersPostCountDB){
userPostCount = req.body.postsCount + 1;
} else {
console.log('There was an error getting the postsCount');
}
});
I thought it should be able to find the userPostCount variable as it's the next level up in the scope chain.
Any ideas how I will be able to access it please? I need that variable to be outside of the function as I will be using it later for other mongoose activities.
I am getting the userPostCount as undefined.
Thanks in advance for the help!
PS: I looked around for other questions on SO but the solution on other answers don't seem to work for me.
Shayan
please use callbacks.
function getPostsCount(callback) {
userPostsModel.findOne({'profileID':req.session.id}, function(err, usersPostCountDB) {
if(usersPostCountDB) {
callback(null, req.body.postsCount + 1);
} else {
console.log('There was an error getting the postsCount');
callback(true, null);
}
});
}
and from outside call it by passing callback function as an argument. so the callback function will be executed right after getPostsCount() returns (finishes)..
getPostsCount(function(error, postsCount) {
if (!error) {
console.log('posts count: ', postsCount);
}
});
You are trying to return a value from a callback function. It's easier if you try to use the result inside the callback function.
Another thing you can do is you can define a function and give it a callback where you can use the value.
function foo(fn){
userPostsModel.findOne({'profileID':req.session.id}, function(err, usersPostCountDB) {
if(usersPostCountDB){
userPostCount = req.body.postsCount + 1;
fn(userPostCount);
} else {
console.log('There was an error getting the postsCount');
}
});
}
foo(function(userPostCount){
alert(userPostCount);
})
If you're using jquery, you can use deferred objects.
http://api.jquery.com/category/deferred-object/

Function returning data from db

I am a newbie to node js and I am using mongoose for my models.
I have a function namde check which has a isNameThere function which receives name string as parameter in it. It checks the Db and looks for the name string which is provided if user exist in this name this.isNameThere will return true
var check= function()
{
this.nameIsThere = false;
this.isNameThere= function(name){
userModel.find({firstname: name},function(err,result){
if(result)
{
this.nameIsThere= true;
}
})
return this.nameIsThere;
}
}
Even if the name exist as you guess the code above will return false because the nature of asynchronous programming. Is there a way to execute the return isNameThere after userModel.find executes. Or any other solution for this situation. Thanks All.
Careful with the semicolons, you forgot some. It´s also good practice in JavaScript to place opening brackets right next to the function header and not in the next line.
You can encapsulate the DB call in a function like this:
function checkForName (callback) {
userModel.find({firstname: name}, callback);
}
checkForName(function (err, result) {
if (result) {
nameIsThere = true;
//do something else
...
}
});
After all, it IS anychronous, so you will not get any synchronous return value.
There´s also another way: Promises. Some libraries for you to check out:
https://github.com/caolan/async
https://github.com/kriskowal/q

Resources