I'm trying to select the distinct values for two fields from my mongodb. Using async.series, I have the following:
function getSettings(target){
var versions = [];
var result = {};
function putResults(key, val){
result[key] = val;
}
async.series(
[
function(callback){
db.get(target).distinct('version', function(err, arr){
if(err) return err;
versions = arr;
callback();
}); //end db call
},
function(callback){
var items;
for(var i = 0; i<versions.length; i++)
{
db.get(target).distinct('instance', {version: versions[i]}, function(err, arr){
items = arr;
putResults(versions[i], items);
}); //end db call
}
}
],
function(err){
if(err) return err;
console.log(result);
}); //end async
}
With this code, I get an "undefined" exception on putResults(versions[i], items). I know it's because versions is out of scope. My question is: how do I get it in scope?
Expected result:
result = {1: [1, 2, 3], 2: [1, 2, 3, 4]}
In our case better use waterfall instead of series, and map, like this
function getSettings(target){
async.waterfall([
function (callback) {
db.get(target).distinct('version', function (err, versions) {
callback(err, versions);
});
},
function (versions, callback) {
async.map(versions, function (version, next) {
db.get(target).distinct('instance', {version: version}, function (err, items) {
var result = {};
result[version] = items;
next(err, result);
});
}, function (err, res) {
callback(err, res);
});
}
], function (err, result) {
if (err) {
return;
}
console.log(result);
});
}
As Alexander mentioned in his comment, if you want to pass the values from the first function to the next one, you should use waterfall instead of series.
As specified in the official documentation regarding waterfall:
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.
If you would like to read more about the various capabilities of async I strongly recommend their official documentation (LINK).
Related
Hello I am having an issue with the following sequence, I need to run multiple queries which build on each other that are in a for loop then once the final result is obtained to implement the result. I am having an issue where my for loop is looping past the query, also I need to stop the code while the findX function is running.
I know this is an async problem but I don't see how I could chain promises, or use the async npm package with needing to loop queries that depend on the result of the previous query. Thanks in advance.
function findX(){
//executes another query
}
function solve(res, connection, info, requestArr, callback){
var parentID = null;
var obj = {};
for (var i = 0; i <= requestArr.length; i++) {
connection.query("SELECT WHERE ", [parentID, requestArr[i]], function(err, results) {
if(results[0]['x']){
var obj = findX(x)
break;
}else{
parentID = results[0]['parentID'];
}
});
}
//Do stuff with obj only after the final result has been set in the for loop
}
You can use Async TimesSeries.
// Pretend this is some complicated async factory
var createUser = function(id, callback) {
callback(null, {
id: 'user' + id
});
};
// generate 5 users
async.times(5, function(n, next) {
createUser(n, function(err, user) {
next(err, user);
});
}, function(err, users) {
// we should now have 5 users
});
So, in your example would be something like this:
var iterations = requestArr.length - 1,
parentID,
obj;
var getResults = function (i, callback) {
connection.query("SELECT WHERE ", [parentID, requestArr[i]], function (err, results) {
if (results[0]['x']) {
obj = findX(x);
callback('done');
}
else {
parentID = results[0]['parentID'];
callback();
}
});
};
async.timesSeries(iterations, function (n, next) {
getResults(n, function (err) {
next(err);
});
}, function (err) {
// use obj
});
This is my code:
var shopId=[1,2,3,4];
async.each(shopId,function (item,callback) {
model.client.query("SELECT categoryId shopId=? ", [shopId], function (err, rows) {
if (rows.length > 0) {
var asyncData = rows;
//categoryId will be in the format [1,2,3,4]
async.each(asyncData,function (item, callback) {
var categoryId = item.categoryId;
async.series([
function (callback) {
model.client.query("select categoryName from category where categoryId = ?", [categoryId], function (err, data) {
callback(null, data);
});
}],
function (err, results) {
item.category = results[0];
callback();
});
},function (err) {
callback(asyncData); //getting data here
});
}
});
},function (err,result) {
res.json(result); //not getting data here
});
I am new to aysnc methods. I am not able to pass the result to final function.
Supposing that there is a shop table, you can join your tables to save a query.
const sql = 'select categoryName from category as c inner join shop as s on c.categoryId = s.categoryId where s.shopId = ?'
then
const shopId = [1, 2, 3, 4];
const finalResult = [];
async.each(shopId, function(item, callback) {
model.client.query(sql, [shopId], function (err, rows) {
// do not forget error handling !
if (rows.length > 0) {
finalResult.push(rows);
return callback();
}
return callback(); // do not forget to return callback in every case
});
}, function(err) { // in async each callback, you only get errors
if (err)
return res.json(err); // or whatever error handling
res.json(finalResult);
});
Also, considering your code, I would advise to name differently your callbacks to avoid errors or forgets. i.e.
async.each([], function(item, cbkGlobal) {
async.each([], function(item, cbkEach) {});
});
And async.series is usefull when you want multiple functions to get execute in order. But here you are executing only one function...
I am using async series to run 2 functions takes2Seconds and function takes5Seconds.Why the final callback not showing any result?
var async = require('async'),
operations = [];
operations.push(takes2Seconds(1,function(){}));
operations.push(takes5seconds(2,function(){}));
async.series(operations, function (err, results) {
if(err){return err;}
console.log(results);
});
function takes2Seconds(a,callback) {
results='Took 2 sec'+a;
callback(null, results);
}
function takes5seconds(b,callback) {
results='Took 5sec'+b;
callback(null, results);
}
It looks like you are pushing two undefined values to operations.
When running async.series the operations array needs to contain function(s) that has callback as it's parameter.
When you do operations.push(takes2Seconds(1, function() {})); you are invoking the takes2Seconds function straight away, and because there is no return statement in the takes2Seconds function, it is pushing undefined to the operations array.
Below, I have added a return statement into your takesXSeconds functions. They now return a function with callback as the parameter, and the returned function gets pushed to the operations array.
I have also remove the callback param from takesXSeconds as it is not needed at this point.
When running async.series(...) now, each function (which we returned from takesXSeconds) gets invoked.
var async = require('async'),
operations = [];
operations.push(takes2Seconds(1));
operations.push(takes5seconds(2));
async.series(operations, function (err, results) {
if(err){return err;}
console.log(results);
});
function takes2Seconds(a) {
var results='Took 2 sec'+a;
return function(callback) {
callback(null, results);
}
}
function takes5seconds(b) {
var results='Took 5sec'+b;
return function(callback) {
callback(null, results);
}
}
First your takes2Seconds function executed then function takes5seconds executed.
var takes2Seconds = function (a, callback) {//first this function executed
results = 'Took 2 sec' + a;
callback(null, results);
};
async.eachSeries(takes2Seconds, takes5seconds, function (err) {
if (err) {
res.json({status: 0, msg: "OOPS! How is this possible?"});
}
res.json("Series Processing Done");
})
var takes5seconds = function (b, callback) { // second this function executed
results = 'Took 5sec' + b;
callback(null, results);
}
Probably just a basic concept of async. I'd like to check on how we can pass the array variable from mongo result(docs) to the second function. In below code the second console.log doesn't give any output.
// Retrieve
var db = require('monk')('localhost/db');
var async = require('async');
console.log('start');
async.waterfall([
function(callback) {
var test = db.get('test');
test.find({}, function(err, docs) {
console.log(docs); //OUTPUT OK
callback(docs);
});
},
function(docs, callback) {
console.log(docs); //NO OUTPUT
}
])
To give you a better understanding of the using multiple callbacks with the async module, let's illustrate this with an example from Seven Things You Should Stop Doing with Node.js of multiple operations with callbacks to find a parent entity, then find child entities that belong to the parent:
methodA(function(a){
methodB(function(b){
methodC(function(c){
methodD(function(d){
// Final callback code
})
})
})
})
With the async module, you can either use the series method to address the use of callbacks for nesting code of multiple methods which may result in Callback Hell:
Series:
async.series([
function(callback){
// code a
callback(null, 'a')
},
function(callback){
// code b
callback(null, 'b')
},
function(callback){
// code c
callback(null, 'c')
},
function(callback){
// code d
callback(null, 'd')
}],
// optional callback
function(err, results){
// results is ['a', 'b', 'c', 'd']
// final callback code
}
)
Or the waterfall:
async.waterfall([
function(callback){
// code a
callback(null, 'a', 'b')
},
function(arg1, arg2, callback){
// arg1 is equals 'a' and arg2 is 'b'
// Code c
callback(null, 'c')
},
function(arg1, callback){
// arg1 is 'c'
// code d
callback(null, 'd');
}], function (err, result) {
// result is 'd'
}
)
Now going back to your example, using the async waterfall method you could restructure your code to
async.waterfall([
function(callback) {
// code a
var test = db.get('test');
test.find({}, function(err, docs) {
if(err) callback(err);
console.log(docs); // OUTPUT OK
callback(null, docs);
});
}], function(err, result) {
if(err) callback(err);
console.log(result); // OUTPUT OK
}
])
You must always handle your errors. Try this!
// Retrieve
var db = require('monk')('localhost/db');
var async = require('async');
console.log('start');
async.waterfall([
function(callback) {
var test = db.get('test');
test.find({}, function(err, docs) {
if(err){
console.log(err);
callback(err);
}
else if(docs){
console.log(docs);
callback(null,docs);
}
else{
console.log("No error no docs");
callback(null);
}
});
},
function(err, docs) {
if(err)
console.log(err);
else{
console.log(docs);
}
}
])
I am currently trying to learn the async module for node.js. Below is an example demonstrating how to use async.forEach.
var async = require('async'),
request = require('request');
var results = {};
function done(err) {
if (err) {
throw err;
}
console.log('done! results: %j', results);
}
var collection = [1, 2, 3, 4];
function iterator(value, callback) {
request.post({
uri: 'http://localhost:8080',
body: JSON.stringify(value)
},
function(err, res, body) {
if (err) {
return callback(err);
}
results[value] = JSON.parse(body);
callback();
});
}
async.forEach(collection, iterator, done);
So async.forEach iterated through each element in the collection, and each element is then passed into "value" in the iterator function...but I don't really understand whats being passed into callback? (which is then executed at the end of iterator)
It will receive an array of the results of the individual operations – whatever you pass to the callback.
Since you don't pass anything to the callback, it will receive an array of undefined.