I am having trouble understanding how to use async/await in my case scenario
MY CODE IS IMPLEMENTED AS FOLLOWS
getUserInfo = function () {
async.waterfall([
getActiveUser, getUsername, update],
function(err , result){
console.log(result)
});
function getActiveUser(callback) {
//i have a db search to find who is active right now
user.find({status: 'active'}, function (err , users){
callback(err, users)
})
}
function getUsername(users, callback) {
async.map(users, getUserInfoXmlrpc, function (err, result) {
callback(err, result);
});
}
function getUserInfoXmlrpc(user, doneCallback) {
// i make a xmlrpc call based on dates
}
}
i am having difficulties make the xmlrpc call for more than one day.
I tried to implement async await creating a function that stores the dates i need in an array but i couldn't make it work.
Any ideas what can i do cause i want to update users one by one as i am doing right now but have to check their info updates based on a xmlrpc call
Related
I have an app.get that will return customer data and customer purchases. Inside this app.get I need run two mysql calls and build a an array to pass back.
How can I execute one query after another and process that data?
app.get('/customer', function (req,res) {
var response1 = [];
var response2 = [];
var processedData = [];
connection.query('QUERY HERE', function(err, rows, fields) {
if (!err){
response.push({rows});
} else {
res.status(400).send(err);
}
});
//for loop 'response' results and perform another query
for (var i = 0; i < response1.length; i++) {
var row = response1[i];
connection.query('QUERY HERE FOR row.customerid', function(err, rows, fields) {
if (!err){
processedData.push({'Customer Name:' : row.customername, 'purchases' : rows});
} else {
res.status(400).send(err);
}
});
}
//Send json back
res.setHeader('Content-Type', 'application/json');
res.status(200).send(JSON.stringify(processedData));
});
There is a very convenient module called async.js that provides a bunch of functions for doing complex async operations. Particularly,
async.waterfall() is great when you need to pass down results from one async operation/task to another.
async.mapSeries() is great when you need to create a new array with results from an array of async operation/tasks.
Let's use both.
If I understood your code correctly, the code would look something similar to
app.get('/customer', function (req, res) {
async.waterfall([
// each task is passed a callback 'cb' as last argument;
// you MUST call it at least and at most once within each task;
// if you pass an error into the callback as the first argument, it will stop the async function
function task1 (cb1) {
//connection.query('QUERY HERE', function(err, rows, fields) {
// if (err) return cb1(err); // stop waterfall() if an error occurred
// cb1(null, rows, fields); // pass results down to next task
//});
connection.query('QUERY HERE', cb1); // shorter version
},
function task2 (rows, fields, cb2) {
// iterate and run async operation over each element in array 'rows'
async.mapSeries(rows, function getPurchases (row, cb3) {
connection.query('QUERY HERE FOR row.customerid', function (err, purchases, fields) {
if (err) return cb3(err); // stop mapSeries() if an error occurred
cb3(null, { 'Customer Name': row.customername, 'purchases': purchases })
});
}, function (err, customers) {
// when mapSeries() is done iterating OR if an error occurred, it will come here
if (err) return cb2(err); // stop waterfall() if an error occurred
cb2(null, customers)
});
// }, cb2); // shorter version
}
], function (err, customers) {
// when waterfall() is done all its tasks OR if an error occurred, it will come here
// handle error and send response here
});
});
Ive been playing around with mongodb in node.js. I have made a basic collection with some data (i know its there ive checked). When I try to run a find() on the collection it returns undefined. I dont know why this is. The code is below:
function get_accounts(){
var MongoClient = mongodb.MongoClient;
var url = "url";
MongoClient.connect(url, function (err, db) {
if (err) {
console.log('Unable to connect to the mongoDB server. Error:', err);
} else {
//HURRAY!! We are connected. :)
console.log('Connection established to database');
var collection = db.collection('accounts');
collection.find().toArray(function(err, docs) {
console.log("Printing docs from Array")
docs.forEach(function(doc) {
console.log("Doc from Array ");
console.dir(doc);
});
});
console.log("mission complete");
}
db.close();
}
);
}
If you know why this is happening i would like to hear your thoughts. thanks! The database is a mongolab hosted database if that makes any difference.
You are getting an undefined value because of the asynchronous nature of node.js, nowhere in your code exists logic that tells the console.log statement to wait until the find() statement finishes before it prints out the documents. You have to understand the concept of callbacks in Node.js. There are a few problems here, though, that you could fix. A lot of people getting started with node have the tendency to nest lots of anonymous functions, creating the dreaded "pyramid of doom" or callback hell. By breaking out some functions and naming them, you can make it a lot cleaner and easier to follow:
var MongoClient = require("mongodb").MongoClient
// move connecting to mongo logic into a function to avoid the "pyramid of doom"
function getConnection(cb) {
MongoClient.connect("your-mongo-url", function(err, db) {
if (err) return cb(err);
var accounts = db.collection("accounts");
cb(null, accounts);
})
}
// list all of the documents by passing an empty selector.
// This returns a 'cursor' which allows you to walk through the documents
function readAll(collection, cb) {
collection.find({}, cb);
}
function printAccount(account) {
// make sure you found your account!
if (!account) {
console.log("Couldn't find the account you asked for!");
}
console.log("Account from Array "+ account);
}
// the each method allows you to walk through the result set,
// notice the callback, as every time the callback
// is called, there is another chance of an error
function printAccounts(accounts, cb) {
accounts.each(function(err, account) {
if (err) return cb(err);
printAccount(account);
});
}
function get_accounts(cb) {
getConnection(function(err, collection) {
if (err) return cb(err);
// need to make sure to close the database, otherwise the process
// won't stop
function processAccounts(err, accounts) {
if (err) return cb(err);
// the callback to each is called for every result,
// once it returns a null, you know
// the result set is done
accounts.each(function(err, account) {
if (err) return cb(err)
if (hero) {
printAccount(account);
} else {
collection.db.close();
cb();
}
})
}
readAll(collection, processAccounts);
})
}
// Call the get_accounts function
get_accounts(function(err) {
if (err) {
console.log("had an error!", err);
process.exit(1);
}
});
You might have to add an empty JSON object inside the find.
collection.find({})
Documentation can be found here.
You must enter this code in an async function and you will be fine here data is the your desired value and you must use promises to not make your code look messy.
var accountCollection = db.collection('accounts);
let data = await accountCollection.find().toArray.then(data=>data).catch(err=>err);
This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 7 years ago.
I'm working on a Node / Mongoose / Express app and I'm having problem getting an array to update while using forEach. I'm not 100% sure what I'm missing. Does this have something to do with synchronous vs asynchronous? I may also just have the return wrong.
router.route('/:slug/episodes')
.get(function(req, res) {
var episodeArray = [];
var episodeDetails = null;
Show.findOne({ 'slug': req.params.slug }, function(err, show) {
if (err) {
res.send(err);
}
var episodes = show.episodes
episodes.forEach(function(episodeID, index) {
Episode.findById(episodeID, function(err, episode) {
if (err) {
res.send(err);
}
episodeArray.push(episode);
});
});
res.send(episodeArray)
});
});
episodeArray isn't getting the episode added in and ends up just being full of null values.
Your code is suffering from a misunderstanding of how async works. First and foremost you should read the Felix Kling link that #elclanrs posted here. SO contributors tend to get tired of answering the same async question over and over. I haven't quite gotten that tired yet, so I'll bite for the purposes of explaining async, but I'll also suggest another option that might be a much better way to solve your problem.
The async solution: There are many ways to await an array of asynchronous operations to complete. async.queue is a popular choice. How it works is you push a set of pending operations into a queue and then tell that queue to wait until all results have been received, at which point you perform your res.send(). The code would look something like this:
var async = require('async');
Show.findOne({
'slug': req.params.slug
}, function(err, show) {
if (err) {
res.send(err);
}
var episodeArray = [];
var queue = async.queue(function(episodeID, callback) {
Episode.findById(episodeID, function(err, episode) {
if (err) {
throw err;
}
episodeArray.push(episode);
callback();
});
});
// Note that forEach is synchronous.
// The tasks will be pushed to the queue before drain()
episodes.forEach(function(episodeID, index) {
queue.push(episodeId);
});
queue.drain = function() {
res.send(episodeArray);
};
});
This is not the best way to solve your problem, this is only for the purpose of demonstrating how you could fix your existing code.
As long as your episodes array is not obscenely huge, then a far better way to query your episodes might be to use mongoDB's $in operator, like so:
Show.findOne({
'slug': req.params.slug
}, function(err, show) {
if (err) {
res.send(err);
}
Episode.find({
_id: {
$in: show.episodes
}
}, function(err, episodes) {
if (err) {
throw err;
}
res.send(episodes);
});
});
Edit:
If you want to go a bit deeper, I should also mention that mongoose supports promises, which you can use to make your code less nested and repetitive, like this:
Show.findOne({
slug: req.params.slug
})
.then(function(show) {
return Episode.find({
_id: {
$in: show.episodes
}
});
})
.then(function(episodes) {
res.send(episodes);
})
// Any errors returned above will short circuit to the middleware next() here
.error(next);
You're not waiting for the asynchronous operations to finish before sending an array back to the client.
Try something like this:
var togo = episodes.length;
var error;
episodes.forEach(function(episodeID, index) {
Episode.findById(episodeID, function(err, episode) {
if (err) {
if (!error)
error = err;
} else
episodeArray.push(episode);
if (--togo === 0)
res.send(error ? error : episodeArray);
});
});
Additionally you should really add a return; if you encounter an error during an async operation, in order to prevent the rest of the code from being executed.
I am trying to learn node and understand how callbacks are working. I am doing this by trying to use the async lib. I am trying to hit my db with 2 separate calls and use the async lib to let me know when my object is ready to build. Here is my aysnc code:
async.parallel({
one: function(callback) {
getUser(id, callback);
},
two: function(callback) {
getUserServices(id, callback);
}
}, function(err, results) {
if(err) {
console.log(err);
new Error();
}
res.json(result);
});
Here are what my functions look like where I am calling the db. There are both basically the same function:
var getUser = function(id, callback) {
var query = client.query('SELECT * FROM USERS WHERE USER_ID=$1', [id]);
query.on('row', function(row, result) {
result.addRow(row);
});
query.on('end', function(result) {
callback(result);
});
};
My code hits the db and returns the user, but when it goes back up to the async code the user is in the err object. What am I doing wrong? How do I properly set up callbacks?
As pointed by damphat, your code should be
//additionally, handle errors
query.on('error', function(err){
callback(err) // The first argument to callback should be an error object
})
query.on('end', function(result){
callback(null, result) //passing null error object
})
I'm new to JS in general, but I am trying to query some data from MongoDB. Basically, my first query retrieves information for the session with the specified session id. The second query does a simple geospacial query for documents with a location near the specified location.
I'm using the mongodb-native javascript driver. All of these query methods return their results in callbacks, so they're non-blocking. This is the root of my troubles. What I'm needing to do is retrieve the results of the second query, and create an Array of sessionIds of all the returned documents. Then I'm going to pass those to a function later. But, I can't generate this array and use it anywhere outside the callback.
Does anyone have any idea how to properly do this?
db.collection('sessions', function(err, collection) {
collection.findOne({'sessionId': client.sessionId}, function(err, result) {
collection.find({'geolocation': {$near: [result.geolocation.latitude, result.geolocation.longitude]}}, function(err, cursor) {
cursor.toArray(function(err, item) {
console.log(item);
});
});
});
Functions are the only thing on javascript that "enclose" scope.
This means that the variable items in your inner callback function are not accessible on the outer scope.
You can define a variable in the outer scope so it will be visible to all the inner ones:
function getItems(callback) {
var items;
function doSomething() {
console.log(items);
callback(items);
}
db.collection('sessions', function(err, collection) {
collection.findOne({'sessionId': client.sessionId}, function(err, result) {
collection.find({'geolocation': {$near: [result.geolocation.latitude, result.geolocation.longitude]}}, function(err, cursor) {
cursor.toArray(function(err, docs) {
items = docs;
doSomething();
});
});
});
});
}
Node.js is asynchronous, so your code should be written to match it.
I've found this model useful. Each nested callback jumble is wrapped in helper function that calls the argument callback 'next' with error code and result.
function getSessionIds( sessionId, next ) {
db.collection('sessions', function(err, collection) {
if (err) return next(err);
collection.findOne({sessionId: sessionId}, function(err, doc) {
if (err) return next(err);
if (!doc) return next(false);
collection.find({geolocation: {$near: [doc.geolocation.latitude, result.geolocation.longitude]}}.toArray(function(err, items) {
return next(err, items);
});
});
});
}
Then in your calling code
getSessionIds( someid, _has_items);
function _has_items(err, items) {
if( err ) // failed, do something
console.log(items);
}