nodejs async each function - javascript

In node js, i am using async function to get the result from another function and store it as array and return the result. But here i am getting empty json as output {}. See the comments inside code block. May i know where i am doing mistake ?
collectSearchResult: function (searchConfig, callback) {
var searchResult = {};
async.each(searchConfig.scope, function (scope, callback) {
var query = {
"query": {
"match": {
"_all": searchConfig.q
}
},
operationPath = scope.url;
this.doSearch(operationPath, query, function (err, results) {
var type = scope.type;
searchResult[type] = results;
// Here i am able to get correct output async
console.log(searchResult);
});
callback();
}.bind(this), function (err) {
// Here it is just returning empty json like {}. this function is called before this.doSearch complete its task
console.log(searchResult);
callback(err, searchResult);
});
}

collectSearchResult: function (searchConfig, callback) {
var searchResult = {};
async.each(searchConfig.scope, function (scope, callback) {
var query = {
"query": {
"match": {
"_all": searchConfig.q
}
},
operationPath = scope.url;
this.doSearch(operationPath, query, function (err, results) {
var type = scope.type;
searchResult[type] = results;
// Here i am able to get correct output async
console.log(searchResult);
//<><><><><><><>
callback(); //you need to place the callback for asynch.each
//within the callback chain of your query, else async.each
//immediately finishes before your data has arrived.
//<><><><><><><>
});
}.bind(this), function (err) {
// Here it is just returning empty json like {}. this function is called before this.doSearch complete its task
console.log(searchResult);
callback(err, searchResult);
});
}

Related

Combine two callbacks into one return

So I have this code:
module.exports.getEstimate = (event, context, callback) => {
var data = JSON.parse(event.body);
lalamove.getQuotation(data ,context, function(err, llm_data){
callback(null,llm_data)
});
};
So it calls lalamove.getQuotation function and returns an object:
{ "totalFee": "108", "totalFeeCurrency": "PHP" }
Now, I have added a new function, that returns this object:
{ "totalFee": "10", "totalFeeCurrency": "PHP" }
from a different function so I thought I should push them in one array and then that is when I would call the callback but it does not work, this is what I have tried
module.exports.getEstimate = (event, context, callback) => {
var data = JSON.parse(event.body);
var response = []
lalamove.getQuotation(data ,context, function(err, llm_data){
const llm_obj = { "lalamove": llm_data }
response.push(llm_obj);
});
inhouse.getQuotation(data ,context, function(err, ih_data){
const ih_obj = {"inhouse": ih_data }
response.push(ih_obj);
});
callback(null,response);
};
and what I want to be the response is like this:
["lalamove": { "totalFee": "108", "totalFeeCurrency": "PHP" },
"inhouse": { "totalFee": "10", "totalFeeCurrency": "PHP" }]
what am I doing wrong?
Your callback(null,response) will not wait for those two callback functions to finish. You can use Promise and use Promise.all(objs).then(function) to wait for all promises finish and run.
Welcome to World's Javascript world - Callback hell.
We have some options for your case: Callback hell, async lib, Promise, async/await...
Callback hell: Call a async function in a callback
module.exports.getEstimate = (event, context, callback) => {
var data = JSON.parse(event.body);
var response = []
lalamove.getQuotation(data, context, function (err, llm_data) {
const llm_obj = { "lalamove": llm_data }
response.push(llm_obj);
// lalamove.getQuotation done!
// call next action
inhouse.getQuotation(data, context, function (err, ih_data) {
const ih_obj = { "inhouse": ih_data }
response.push(ih_obj);
// inhouse.getQuotation done!
// call the last action
callback(null, response);
});
});
};
Async lib: async
You can use waterfall function to do actions in order, and parallel if order is not matter.
module.exports.getEstimate = (event, context, callback) => {
var data = JSON.parse(event.body);
var response = []
async.parallel([
function (next) {
lalamove.getQuotation(data, context, function (err, llm_data) {
// TODO: check err object
const llm_obj = { "lalamove": llm_data }
response.push(llm_obj);
// lalamove.getQuotation done!
// do next action
next();
});
},
function (next) {
inhouse.getQuotation(data, context, function (err, ih_data) {
const ih_obj = { "inhouse": ih_data }
response.push(ih_obj);
// inhouse.getQuotation done!
// do next action
next()
});
}
], function (err) {
// TODO: check err object
// call the last action
callback(null, response);
});
};
Try wrapping two quotation calls in Promise, then utilise Promise.all to wait for both of them to be completed, then return the result to the callback
module.exports.getEstimate = (event, context, callback) => {
let data = JSON.parse(event.body);
// wrap quotation calls in `Promise`
Promise.all([
new Promise(resolve => lalamove.getQuotation(data, context, (err, lalamove) => resolve({ lalamove }))),
new Promise(resolve => inhouse.getQuotation (data, context, (err, inhouse ) => resolve({ inhouse }))),
]).then(response => {
// return the result back to `callback`
callback(null, response);
})
};
You could also try using util.promisify and the async / await syntax.
For example:
const util = require("util");
module.exports.getEstimate = async (event, context, callback) => {
let data = JSON.parse(event.body);
try {
let response = await Promise.all([ util.promisify(lalamove.getQuotation)(data, context),
util.promisify(inhouse.getQuotation)(data, context) ]);
callback(null, response);
} catch (err) {
callback(err);
}
};
We can also do something similar, but without async / await:
const util = require("util");
const getEstimate = (event, context, callback) => {
let data = JSON.parse(event.body);
Promise.all([util.promisify(lalamove.getQuotation)(data, context),
util.promisify(inhouse.getQuotation)(data, context)])
.then(response => callback(null, response))
.catch(err => callback(err));
};

The value from the function is returned after the variable is output to the console

In the loop, I call the function and pass it the identifier, after which the function should return the vacancy to me.
const Profile = require('upwork-api/lib/routers/jobs/profile.js').Profile;
...
let jobsDetails = [];
for (let valueKeys of Object.values(arrayOfJobs)) {
getJobDitalis(api, valueKeys, (error, data) => {
console.log(data.profile)
jobsDetails.push(`${data.profile}`);
});
console.log(jobsDetails)
}
...
function getJobDitalis(api, key, callback) {
const profile = new Profile(api);
profile.getSpecific(key, (error, data) => {
callback(error, data);
});
}
But for some reason, first an empty object is displayed in the console, and then only information about the vacancy. That is, console.log (jobsDetails) is fired first, and then only console.log (data.profile)
[]
[]
[]
{job}
{job}
{job}
Why it happens? What is my mistake?
Using async await you can make your async task synchronous.
I don't know about your code structure, but I tried to make a solution from the given data.
function getJobDitalis(api, key, callback) {
const profile = new Profile(api);
profile.getSpecific(key, (error, data) => {
callback(error, data);
});
}
...
(async ()=> {
let jobDetails = [];
for (let valueKeys of Object.values(arrayOfJobs)) {
const profile = await new Promise(resolve=>{
getJobDitalis(api, valueKeys, (error, data) => {
console.log(data.profile)
resolve(data.profile);
});
})
jobDetails.push(profile)
}
console.log(jobDetails);
//do your task with jobDetails
})()
I made IIFE async function.
Flow (let's assume a single iteration of the for loop):
getJobDetalis(api, key, callback)
profile.getSpecific(key, func)
func(error, data)
callback(error, data)
console.log(data.profile)
console.log(jobsDetails)
However, profile.getSpecific does not necessarily wait for func to complete.
And since the 1st print is executed from the call-stack of func, it will not necessarily occur before the 2nd print.

Pass parameter to callback function inside a forEach loop?

I am working in client-side JavaScript. I want to run a callback inside a forEach loop, and pass the value of the variable in the forEach loop to the callback.
Here is my (broken) code:
var tags = ['foo', 'bar'];
var displayTag = function(error, result) {
if (error) {
console.warn("Error from server", error);
}
else {
console.log(tag);
}
};
tags.forEach(function(tag) {
if (is_project) {
Project.addTag(visualisation_id, tag, displayTag);
} else {
Page.addTag(page_id, tag, displayTag);
}
});
When I run this I'm getting a warning that tag in console.log(tag) is undefined.
How can I pass the value of tag to the callback? I'm sure this is simple!
Perhaps you can use a Higher-Order Function (a function that returns a function):
var tags = ['foo', 'bar'];
var createDisplayTagCallback = function(tag) {
return function (error, result) {
if (error) {
console.warn("Error from server", error);
}
else {
console.log(tag);
}
}
};
tags.forEach(function (tag) {
if (is_project) {
Project.addTag(visualisation_id, tag, createDisplayTagCallback(tag));
} else {
Page.addTag(page_id, tag, createDisplayTagCallback(tag));
}
}); </script>
Haven't tested this code though...
You must have a callback in addTag
this.addTag(visualisation_id, tag, callback) {
callback(error, result)
}
Change it to
this.addTag(visualisation_id, tag, callback) {
callback(error, result, tag)
}

Cannot access db result in Nodejs, it always returns null

var robject=[];
async.waterfall([
function (callback) {
for(var i in serial){
Router.find({},{r_serial_no:serial[i]},function (err,routerData) {
robject = robject.concat(routerData);
});
}
console.log('Robject= '+robject); //THIS RETURNS NULL
callback(null, robject);
},
function (blogs, callback) {
res.render('index', {dispatched_data:dispatched_data });
callback(null, 'Ended..' );
}
], function (err, result) {
console.log(result);
});
this is my waterfall model, here i need to access the robject from schema.find method to outside of that method. but it always return null..
how to access that??
You have the syntax error:
for(var i in serial){
Router.find({},{r_serial_no: i},function (err,routerData) {
robject = robject.concat(routerData);
});
}
the "for" loop defines "i" as next item in the array each iteration
The problem I see here is in for...in loop. Your callback will be fired even if your process i.e. Router.find is not completed. You can try below code, It might help.
Unlike your serial object please create a array called serials.
var robject=[];
async.waterfall([
function (callback) {
async.each(serials,
function(serial, localCb){
Router.find({},{r_serial_no:serial},function (err,routerData) {
robject = robject.concat(routerData);
localCb()
});
},
function(err){
console.log('Robject= '+robject);
callback(null, robject);
}
);
},
function (blogs, callback) {
res.render('index', {dispatched_data:dispatched_data });
callback(null, 'Ended..' );
}
], function (err, result) {
console.log(result);
});

async.js return value form last function

I would like create find method in my User object. This function should returns user. But for this example it returns only the text.
But I don't know how return value for waterfall.
When I run
console.log(User.find("575578a9f95d6de1354327ef"));
I got 'undefined' in my output, but I except 'function find shoud return this value', What I should to do if I want get 'function find shoud return this value' text on my output
User = {
collectionName: 'users',
find: function(id){
async.waterfall(
[
function(callback) {
MongoClient.connect('mongodb://127.0.0.1:27017/lingogo', function(err,db) {
if(err) { throw err}
callback(null, db, id);
});
},
function(db,id, callback) {
var collection = db.collection(User.collectionName);
collection.find({'_id': ObjectID(id)}).toArray(function (err, result) {
if (err) { throw err };
if (result[0] && result[0]._id != '') {
return callback(null,result[0]);
}
return callback(null,null);
})
},
],
function (err, user) {
return 'function find shoud return this value';
}
);
}
}
console.log(User.find("575578a9f95d6de1354327ef"));
Function find must have a callback too, that you call in a callback of waterfall. You cannot return a value synchronously from an asynchronous function.
find: function (id, callback) {
async.waterfall(..., function (...) {
callback(null, return_value);
});
}
That should be called like
User.find("575578a9f95d6de1354327ef", function (err, return_value) {
console.log(return_value);
});

Categories