var user_id = '98-XXXXXXXX'
Contact.find({user_id: user_id})
.exec(function (err, results) {
if (err) {
return next(err);
}
var finalArray = [];
for(var i = 0; i< results[0].Total; i++) {
if(results[0].Contacts[i].name != "SPAM") {
for(var j = 0; j < results[0].Contacts[i].phoneNumbers.length; j++){
var number = results[0].Contacts[i].phoneNumbers[j].number
number = number.replace(/ /g,'');
var user_id = number.substr(number.length - 10);
Login.find({user_id:user_id})
.exec(function(err,results) {
if(err) {
return next(err); }
var intCount = results.length;
if (intCount > 0)
{
console.log('called')
finalArray.push(results[0])
console.log(finalArray)
}
});
}
}
//console.log(i,results[0].Total - 1);
//if(i == results[0].Total - 1)
}
console.log('Ended Here',finalArray)
var responseTosend = {"message":finalArray,"user_id":user_id}
return res.send(responseTosend);
});
EndedHere[] this is coming up first empty, after that i got the result of login.find query which is correct. Any ideas how to get the finalArray after all the calculation.
Thanks in advance
Since the functions are returning promises within the loops, the code execution has to wait till all those promises are resolved. Consider using Promise.all or Promise.map to wait. Reference
As already mentioned, a structure like this, will not return the results, but the intermediate functions or objects before they are finished, since nodejs does not know it should await the results first.
const x = [1,2,3]
let results = []
for (let i = 0; i < x.length; i++){
results.push(someAsyncJobLikeADatabaseCall(x[i]))
}
// this will not return the results, but the intermediate async objects/functions
console.log(results)
Here is a better version using promises and the .map function. Notice, how we replaced the for loop with .map() (which you could see as a shorthand for .forEach + push() or for() + push(). Mongoose returns Promises if configured right, so you don't even have to manually define them and we can directly return them in .map.
const x = [1,2,3]
let results = []
async function getAsyncResults(array){
// map returns an array, this time, an array of promises
const promises = x.map(number => someAsyncJobLikeADatabaseCall(number))
// Promise.all resolves, if all promises in the array have been resolved
return Promise.all(promises)
}
try {
let results = await getAsyncResults(x)
// this will return the results you expect.
console.log(results)
} catch (err) {
console.log('Some error', err)
}
Related
How can I get a return after I loop through all my firebase results to properly finish my cloud function?
var count = 0;
return ref.child('/records').once('value').then(snap=>{
snap.forEach(snapChild=>{
var ucName = ${snapChild.val().name.toUpperCase();
var update = {'name':ucName);
ref.child(`/records/${snapChild.key}`).update(update).then(()=>{
count++;
res.write(`<p>${ucName} updated</p>`);
});
})
}).then(()=>{
return res.end(`<p>end of list (${count} records)</p>`);
})
It actually does what it is supposed to do but the counter stays at 0 and I get an error 'write after end' - I guess due to the missing return from forEach.
It's because the last then callback is called, when snap children is not processed. You need to use Array#map to produce an array of promises, and Promise.all to wait until all promises are not resolved:
var count = 0;
return ref
.child('/records').once('value')
.then(snap => {
let ops = snap.map(snapChild => {
var ucName = ${snapChild.val().name.toUpperCase();
var update = {'name':ucName);
return ref.child(`/records/${snapChild.key}`).update(update).then(() => {
count++;
res.write(`<p>${ucName} updated</p>`);
});
});
return Promise.all(ops);
})
.then(()=>{
return res.end(`<p>end of list (${count} records)</p>`);
});
I'm trying to write a function (using WebdriverJS lib) that iterates through a list of elements, checks the names and build an xpath locator that corresponds to that name. I simplified xpath locators here, so don't pay attention.
The issues I'm facing here are:
1) Calling this function returns undefined. As far as I understand, this is because the return statement is not in its place, but:
2) Placing it in the correct place where a synchronous code would normally work, doesn't work for async promises, hence calling this function will return the same undefined, but because the return statement fires before the "driver.findElement" statement.
How should I use the return statement here, if I want to get createdTask variable as a result of calling this function?
var findCreatedTask = function() {
var createdTask;
driver.findElements(By.xpath("//div[#id='Tasks_Tab']")).then(function(tasks) {
for (var index = 1; index <= tasks.length; index++) {
driver.findElement(By.xpath("//div[#id='Tasks_Tab'][" + index + "]//div[#class='task-title']")).getText().then(function(taskTitle) {
if (taskTitle == "testName") {
createdTask = "//div[#id='Tasks_Tab'][" + index + "]";
return createdTask;
}
});
}
});
};
You could first get all the texts with promise.map and then get the position with indexOf :
var map = webdriver.promise.map;
var findCreatedTask = function() {
var elems = driver.findElements(By.xpath("//div[#id='Tasks_Tab']//div[#class='task-title']"));
return map(elems, elem => elem.getText()).then(titles => {
var position = titles.indexOf("testName") + 1;
return "//div[#id='Tasks_Tab'][" + position + "]";
});
}
Here you go, I cleaned it up a bit. This will actually return an error if one is experienced in the nested promises:
var findCreatedTask = function() {
var Promise = require('bluebird');
var createdTask;
return driver.findElements(By.xpath("//div[#id='Tasks_Tab']"))
.then(function(tasks) {
return Promise.map(tasks, function(task){
return driver.findElement(By.xpath("//div[#id='Tasks_Tab'][" + index + "]//div[#class='task-title']")).getText()
}).then(function(taskTitles){
for (let i = 0; i < taskTitles.length; i++){
if(taskTitles[i] === 'testName'){
createdTask = "//div[#id='Tasks_Tab'][" + i + "]";
return createdTask;
}
}
});
});
};
You call it using
findCreatedTask.then(function(res){
//do your thing
}).catch(function(err){
console.error(err.stack);
});
You will not be able to return the value that you want from this function because when this function returns, the value is not defined yet.
This is not a problem that you try to return the value in the wrong place, but that you try to access it at the wrong time.
You have two options: you can either return a promise from this function, or this function can take a callback that would be called when the value is available.
Examples
This is not tested but should give you an idea on how to think about it.
Promise
Version with promise:
var findCreatedTask = function (callback) {
var createdTask;
return new Promise(function (resolve, reject) {
driver.findElements(By.xpath("//div[#id='Tasks_Tab']")).then(function(tasks) {
for (let index = 1; index <= tasks.length && !createdTask; index++) {
driver.findElement(By.xpath("//div[#id='Tasks_Tab'][" + index + "]//div[#class='task-title']")).getText().then(function(taskTitle) {
if (taskTitle == "testName") {
createdTask = "//div[#id='Tasks_Tab'][" + index + "]";
resolve(createdTask);
}
});
}
});
});
};
and then you call it with:
findCreatedTask().then(function (createdTask) {
// you have your createdTask here
});
Callback
Version with callback:
var findCreatedTask = function (callback) {
var createdTask;
driver.findElements(By.xpath("//div[#id='Tasks_Tab']")).then(function(tasks) {
for (let index = 1; index <= tasks.length && !createdTask; index++) {
driver.findElement(By.xpath("//div[#id='Tasks_Tab'][" + index + "]//div[#class='task-title']")).getText().then(function(taskTitle) {
if (taskTitle == "testName") {
createdTask = "//div[#id='Tasks_Tab'][" + index + "]";
callback(null, createdTask);
}
});
}
});
};
and then you call it with:
findCreatedTask(function (err, createdTask) {
// you have your createdTask here
});
More info
You can read some other answers that explain how promises and callbacks work if you're interested to know ore about it:
A detailed explanation on how to use callbacks and promises
Explanation on how to use promises in complex request handlers
An explanation of what a promise really is, on the example of AJAX requests
An explanation of callbacks, promises and how to access data returned asynchronously
I'm trying to read multiple data from database, put them into an array, and deal with the array. The code looks like this:
var array = [];
// first for loop
for (var i = 0; i < 10; i++) {
db.read(i, function(rs) { // read data from database and put it into array
array.push(rs);
}
}
// second for loop
for (int i = 0; i < 10; i++) {
console.log(array[i]);
}
However, this piece of code will not work because the second for loop will execute before the first loop ends. Is there any good solutions? BTW, I've used promise like this:
var array = [];
var promise = new Promise(function(resolve, reject) {
for (var i = 0; i < 10; i++) {
db.read(i, function(rs) { // read data from database and put it into array
array.push(rs);
}
}
resolve(array);
};
promise.then(function(array) {
for (int i = 0; i < 10; i++) {
console.log(array[i]);
}
};
It doesn't work either, it seems that the resolve will not wait until all the db read operations finish. So when will the resolve wait until all the previous code finish?
the best way to complete your requirement use async.
var name = ["shekhar", 'vishal', "param", "abhishek", 'amit'];
for (var i = 0; i < 5; i++) {
var item = name[i];//execute your first loop here
}
async.eachSeries(name, processData, function (err) {
if (err) {
res.json({status: 0, msg: "OOPS! How is this possible?"});
}
res.json("Series Processing Done");
})
function processData(item, callback) {
//execute your second loop here
});
Using promises, the asynchronous block of code should return a promise.
The database read function should return a promise, and can easily be rewritten as
function readFromDatabase(i){
return new Promise(function(resolve, reject){
db.read(i, function(rs) { // read data from database and put it into array
resolve(rs);
}
});
}
You then run your loop and get all the values from the database, await the promises and access the result in the .then() function where you can iterate of them.
//We create an empty array to store our promises
var arrayOfPromises = [];
for (var i = 0; i < 10; i++) {
//We push each promise to the array
arrayOfPromises.push(readFromDatabase(i));
}
//We wait until all promises have
Promise.all(arrayOfPromises)
.then(function(arrayOfResults){
//arrayOfResults is an array with the resolved values from the 'readFromDatabase' function
})
.catch(function(){
//Handle errors
});
So let's say I have the following for loop
for(var i = 0; i < array.length; i++){
Model.findOne({ _id = array[i].id}, function(err, found){
//Some stuff
});
}
How do I make this code work? Every time I run it I get array[i] = undefinedbecause the mongo-db query is asynchronous and the loop has already iterated 5 times by the time the first query is even completed. How do I go about tackling this issue and waiting for the query to complete before going on to the next iteration?
This doesn't specifically answer your question, but addresses your problem.
I'd use an $in query and do the filtering all at once. 20 calls to the db is pretty slow compared to 1:
// grab your ids
var arrayIds = myArray.map(function(item) {
return item._id;
});
// find all of them
Model.find({_id: {$in: arrayIds}}, function(error, foundItems) {
if (error) {
// error handle
}
// set up a map of the found ids
var foundItemsMap = {};
foundItems.forEach(function(item) {
foundItemsMap[item._id] = true;
});
// pull out your items that haven't been created yet
var newItems = [];
for (var i = 0; i < myArray.length; i++) {
var arrayItem = myArray[i];
if ( foundItemsMap[arrayItem._id] ) {
// this array item exists in the map of foundIds
// so the item already exists in the database
}
else {
// it doesn't exist, push it into the new array
newItems.push(arrayItem);
}
}
// now you have `newItems`, an array of objects that aren't in the database
});
One of the easiest ways to accomplish something like you want is using promises. You could use the library q to do this:
var Q = require('q');
function fetchOne(id) {
var deferred = Q.defer();
Model.findOne({ _id = id}, function(err, found){
if(err) deferred.reject(err);
else deferred.resolve(found);
});
return deferred.promise;
}
function fetch(ids, action) {
if(ids.length === 0) return;
var id = ids.pop();
fetchOne(id).then(function(model) {
action(model);
fetch(ids, action);
});
}
fetch([1,2,3,4,5], function(model) { /* do something */ });
It is not the most beautiful implementation, but I'm sure you get the picture :)
Not sure if this is the right way, it could be a bit expensive but this how i did it.
I think the trick is to pull all your data and then looking for an id match.
Model.find(function(err, data) {
if (err) //handle it
for (var i=0; i<array.length; i++) {
for (var j=0; ij<data.length; j++) {
if(data[j].id == array[i].id) {
// do something
}
}
}
}
The gist of my code is as follows:
function checkWinRate(array){
var winCount = 0;
var totalCount = 0;
db.collection.find(query).each( function(foo){
if (condition){
winCount++;
}
totalCount++;
db.close();
});
return winCount/totalCount;
}
The obvious problem here is that at the return statement, both winCount and totalCount are still equal to 0. I am trying to use Q promise library to sort out a sequence of events, but I am not sure exactly how to implement it. Can anybody tell me what to do with Q?
EDIT
ID: 10T Error here. I forgot I was using the monk wrapper. For those who want to know what I did to solve this, monk has a nice way of dealing with promises in the following way:
function checkWinRate(array){
var winCount = 0;
var totalCount = 0;
var promise = db.collection.find(query).each( function(foo){
if (condition){
winCount++;
}
totalCount++;
db.close();
});
promise.success(function(){
return winCount/totalCount);
}
}
The function you are passing in each() will be executed asynchronously. You need to immediately return a promise and resolve it after all the iterations are finished:
var deferred = Q.defer();
db.collection.find(query).each(function(err, item) {
if (err) {
deferred.reject(err);
}
if (condition){
winCount++;
}
totalCount++;
if(item == null) {
// iterations are finished
deferred.resolve(winCount/totalCount);
db.close();
}
});
return deferred.promise;
To obtain value you need to call checkWinRate this way:
checkWinRate(array).then(function (rate) {});