Issue after nesting async.eachOfSeries - javascript

I was using nested for loops to get a thing done. I figured out that the HTTP call and the database query are asynchronous and eventually gives undesired output. So I used async.eachOfSeries. The execution gets stopped just after it enters the inner most async.eachOfSeries in the first iteration itself. I suppose I have made some error is structuring the code and in returning the callbacks. I need to know basically two things from you:
What is stopping the execution of the code in the first iteration itself?
Will this approach solve the asynchronicity issue? Or is there any better approach to solve it?
Your help would be appreciated.
async.eachOfSeries(res, function (value, camp, callback) {
let _id = res[camp]._id;
let arr = res[camp].campaignID;
async.eachOfSeries(arr, function (value1, i, callback) {
let users = arr[i].users;
let id = arr[i].id;
let loop = Math.ceil(users / 1000);
let limit = 0,
offset = 0;
for (let j = 0; j < loop; j++) {
if (users > 1000) {
limit = 1000;
users -= limit;
} else {
limit = users;
}
console.log(limit + " limit " + offset + " offset");
var start = Date.now();
while (Date.now() < start + 100) {}
const request = mailjet
.get("messagesentstatistics")
.request({
"CampaignID": id,
"AllMessages": true,
"Limit": limit,
"Offset": offset
})
request
.then((result) => {
let data = result.body.Data;
var loop = 0;
async.eachOfSeries(data, function (value2, val, callback) {
console.log("ayyuuuuu");
let jsonObj = data[val];
let email = jsonObj.ToEmail;
jsonObj['retailer'] = res[camp].retailer;
jsonObj['summary'] = 'f';
let tempObj = {};
tempObj[id] = jsonObj;
let options = {
new: true
};
let campId = id;
User.addCampaignResponse(email, campId, tempObj, options, function (err, results) {
if (err) {
throw err;
} else {
Campaign.updateResponse(_id, function (err, results2) {
if (err)
throw err;
}) // console.log(results);
}
})
}, function (err) {
callback();
})
})
.catch((err) => {
console.log(err);
})
offset += limit;
}
}, function (err) {
callback();
})
}, function (err) {
callback(undefined, "doneeeeee");
})

Related

Nested For not working in Async Function - React Native App

I have a function which I get some data from database and I want to create a structured JSON object and pass it to an API.
I can get the data from database and create an JSON object, but for some reason the FOR loop is only running after all the routine. I've already spent sometime trying to solve this, but I couldn't. If someone could help me, it would be really appreciated.
This is the full function:
async function SendFullAudit() {
//Method to call from API
console.log('INSIDE SEND FULL AUDIT');
var auditHeader = { AUDITHEADER: {}, AUDITITEMS: {} };
var auditItemToSend = { PICTURES: {} };
var ItemstoAdd = [];
var Picture = {
content: {},
};
db.transaction(async(tx) =>
{
console.log('Get audits results' + auditsTosend[0].ID);
tx.executeSql('select id, audit_item_id,od,comments,status_id,scanned_code,findings from tb_audits where audit_id = ? ', [auditsTosend[0].ID], (tx, results) => {
for (let i = 0; i < results.rows.length; i++) {
tx.executeSql('select AUDIT_ID,PICTURE_URI from tb_AuditPictures where audit_ID =?', [results.rows.item(i).ID], async (tx, resultsPic) => {
console.log('Pictures Result inside full audit:' + resultsPic.rows.length + ' for item:' + results.rows.item(i).ID);
if (resultsPic.rows.length > 0) {
for (let j = 0; j < resultsPic.rows.length; j++) {
var tempPic = resultsPic.rows.item(j);
console.log('Inside resultsPic.rows.length > 0' + tempPic.PICTURE_URI);
auditPictures = await RNFS.readFile(tempPic.PICTURE_URI, 'base64').then(res => {
Picture.content = res;
ItemstoAdd[i].PICTURES = Picture;
console.log("ItemstoAdd:" + ItemstoAdd[i].ID + " ITEM PICTURE:" + Picture.content);
});
}
}
});
auditItemToSend = results.rows.item(i);
ItemstoAdd.push(auditItemToSend);
}
auditHeader.AUDITHEADER = auditsTosend;
auditHeader.AUDITITEMS = ItemstoAdd;
console.log('\n');
console.log('stringFy headerItem:' + JSON.stringify(auditHeader));
});
}); }
When the routine runs, the first console shown is this console.log('Get audits results' + auditsTosend[0].ID); , followed by this console.log('stringFy headerItem:' + JSON.stringify(auditHeader)); which is after the FOR loop, and at this moment the auditHeader doesn't have the Pictures yet.
After that it runs the FOR loop, how could I get auditHeader var with the the Pictures which is added inside the for loop?
I've spent a lot of time with this problem, and read a lot about promises.
Now the query is using a join with the URI from database, and inside the function I'm calling a for loop which converts the URI into a base64, using react-native-fs. But the ReadFile function only returns after the main function. How could I make the GetFullAuditData await the ReadFile functions end?
async function ReadFile(PICTURE_URI)
{
let data = await RNFS.readFile(PICTURE_URI, 'base64').then(res=>{ return new Promise((resolve,reject) => {resolve(res),reject((err)=>console.log("Error:"+err))})});
data = String(data);
//base64Pict.push("PIC:"+data);
console.log("ReadFile:"+data);
}
function GetFullAuditData()
{
//var auditHeader = auditsTosend;
var auditHeader = { AUDITHEADER: {}, AUDITITEMS: {PICTURES: {content: {}}} };
var auditItemToSend = { PICTURES: {} };
var ItemstoAdd = [];
var Picture = {content: {}};
console.log('GET FULL AUDIT');
return new Promise((resolve, reject) =>
{
console.log('CREATE PROMISE');
db.transaction(async(tx) => {
// tx.executeSql('select id, audit_item_id,od,comments,status_id,scanned_code,findings,"" as PICTURES from tb_audits where audit_id = ? ', [auditsTosend[0].ID], (tx, results) => {
tx.executeSql('select a.id, a.audit_item_id,a.od,a.comments,a.status_id,a.scanned_code,a.findings,ap.PICTURE_URI, "" AS PIC64 from tb_audits a left join tb_AuditPictures ap on a.ID = ap.AUDIT_ID where a.AUDIT_ID = ? ', [auditsTosend[0].ID], (tx, results) => {
console.log("Query completed");
var len = results.rows.length;
var items =[];
for (let i = 0; i < len; i++)
{
let row = results.rows.item(i);
console.log('RESULTS LENGTH:'+row.PICTURE_URI)
if (row.PICTURE_URI!=null)
{
var PIC64 = ReadFile(row.PICTURE_URI);//ReturnPicture64(row.PICTURE_URI);
console.log('PIC64:'+ PIC64);
}
items.push(results.rows.item(i));
console.log('Record:'+ row.PIC64);
}
resolve(items)
reject((err)=>console.log("ERROR:"+err))
});
});
});
}
Your code suffers from the callback hell issue. You know, async callbacks that you are passing don't await! It will help if you handle them manually by converting callbacks to Promises.
async function SendFullAudit() {
//Method to call from API
console.log('INSIDE SEND FULL AUDIT');
var auditHeader = { AUDITHEADER: {}, AUDITITEMS: {} };
var auditItemToSend = { PICTURES: {} };
var ItemstoAdd = [];
var Picture = {
content: {},
};
db.transaction(async(tx) =>
{
console.log('Get audits results' + auditsTosend[0].ID);
const {tx, results} = await new Promise((resolve, reject)=>{
tx.executeSql('select id, audit_item_id,od,comments,status_id,scanned_code,findings from tb_audits where audit_id = ? ', [auditsTosend[0].ID], (tx, results) => {
resolve({tx, results})
})
})
// tx.executeSql('select id, audit_item_id,od,comments,status_id,scanned_code,findings from tb_audits where audit_id = ? ', [auditsTosend[0].ID], (tx, results) => {
for (let i = 0; i < results.rows.length; i++) {
const {tx, resultsPic} = await new Promise((resolve)=>{
tx.executeSql('select AUDIT_ID,PICTURE_URI from tb_AuditPictures where audit_ID =?', [results.rows.item(i).ID], (tx, resultsPic) => {
resolve({tx, resultsPic})
})
})
// tx.executeSql('select AUDIT_ID,PICTURE_URI from tb_AuditPictures where audit_ID =?', [results.rows.item(i).ID], async (tx, resultsPic) => {
console.log('Pictures Result inside full audit:' + resultsPic.rows.length + ' for item:' + results.rows.item(i).ID);
if (resultsPic.rows.length > 0) {
for (let j = 0; j < resultsPic.rows.length; j++) {
var tempPic = resultsPic.rows.item(j);
console.log('Inside resultsPic.rows.length > 0' + tempPic.PICTURE_URI);
auditPictures = await RNFS.readFile(tempPic.PICTURE_URI, 'base64').then(res => {
Picture.content = res;
ItemstoAdd[i].PICTURES = Picture;
console.log("ItemstoAdd:" + ItemstoAdd[i].ID + " ITEM PICTURE:" + Picture.content);
});
}
}
// });
auditItemToSend = results.rows.item(i);
ItemstoAdd.push(auditItemToSend);
}
auditHeader.AUDITHEADER = auditsTosend;
auditHeader.AUDITITEMS = ItemstoAdd;
console.log('\n');
console.log('stringFy headerItem:' + JSON.stringify(auditHeader));
// });
});
}
To be honest, there is no easy way to try your code out to testify the result. I've tried my best to show the way. Hope it would be helpful.

How to get data with axios from all api pages?

What am I doing wrong? I want to get data from all pages in api. After adding the while it stopped working, but I don't know how to start the page loop differently!
getCustomers: function() {
let url = '/crm/customer/';
return axios.get(url).then((response) => {
this.customers = response.data.results;
if (this.customers.length === 100) {
let i = 2;
axios.get('/crm/customer/?page=' + i).then((response) => {
this.c = response.data.results;
i += 1;
for (let item of this.c.values()) {
this.customers.push(item);
}
while (this.c.length === 100) {
axios.get('/crm/customer/?page=' + i).then((response) => {
this.c = response.data.results;
i += 1;
for (let item of this.c.values()) {
this.customers.push(item);
}
}).catch( error => {}).finally(() => (global_waiting_stop()));
}
}).catch( error => {}).finally(() => (global_waiting_stop()));
}
}).catch( error => {}).finally(() => (global_waiting_stop()));
},
What I would do is, first, use an async function called getPageOfResults:
async function getPageOfResults(page) {
const response = await axios.get('/crm/customer/?page=' + page);
return response.data.results; // .results.values()? I don't know
}
Then, inside another async function, you can have a loop that does what you want:
async function getAllResults() {
const customers = [];
let lastResultsLength = 100;
let page = 1;
while (lastResultsLength === 100) {
const newResults = await getPageOfResults(page);
page++;
lastResultsLength = newResults.length;
customers = customers.concat(newResults);
}
return customers;
}
So you've got a variable keeping track of what page your on, and you keep on getting new pages until you get a page with less than 100 results.
You're adding all the results together with the concat function, and returning the entire list at the end.

Chaining Promises Cross Function Javascript

I suspect I've fundementally misunderstood Javascript promises, any ideas?
I have a pretty function that queries a database containing music that looks like this:
function searchDatabaseForTrack(query,loadedResults){
loadedResults = loadedResults || [];
desiredResults = 100;
if (loadedResults.length < desiredResults) {
try {
databaseApi.searchTracks(query, {"offset":loadedResults.length, "limit":"50", }).then(function(data){
i=0
if (data.tracks.items.length == 0) {
console.log(`Already loaded all ${loadedResults.length} tracks!`)
console.log(loadedResults)
return loadedResults;
}
else {
for (thing in data.tracks.items){
loadedResults.push(data.tracks.items[i]);
i=i+1;
}
console.log(loadedResults.length, " tracks collected");
searchDatabaseForTrack(query,loadedResults)
}
});
} catch(err) {
console.log("ERROR!", err)
console.log(loadedResults)
return loadedResults;
}
} else {
console.log(loadedResults)
return loadedResults;
}
}
And then a bit later, I try to call and use the data retrieved.
function getArtistTracks(artistName){
searchDatabaseForTrack(artistName).then(function(data){
console.log(songs);
songs.sort(function(a,b){
var c = new Date(a.track.album.release_date);
var d = new Date(b.track.album.release_date);
return d-c;
});
console.log("songs", songs);
var newsongs=[];
i=0
for (song in songs) {
newsongs.push(songs[i].track.uri);
i++
};
return newsongs;
});
}
What I'm trying to do is get the second function "getArtistTracks" to wait for the completion of the query in the first function. Now I could just call the databaseApi.searchTracks directly, but there's a limit of 50 tracks returned per result — which kind of screws me over.
searchDatabaseForTrack().then(...) shouldn't work since searchDatabaseForTrack() doesn't return a promise, so you can either return a promise or use an async function.
instead of a recursive function, you could simply call databaseApi in a for loop,
the desiredResult should be an argument and not hardcoded in the function,
async function searchDatabaseForTrack(query, desiredResults){
let loadedResults = [], data, currOffset = 0;
const iterations = Math.ceil(desiredResults / 50);
for(let n = 0 ; n < iterations; n++){
cuurOffset = n * 50;
data = await databaseApi.searchTracks(query, {"offset":currOffset, "limit":"50", });
if (data.tracks.items.length == 0) {
console.log(`Already loaded all ${loadedResults.length} tracks!`)
console.log(loadedResults)
return loadedResults;
}
else {
loadedResults = loadedResults.concat(data.tracks.items);
console.log(loadedResults.length, " tracks collected");
}
}
return loadedResults;
}
the rest should be fine as long as you add .catch() to handle errors ( as mentionned in previous answer ) which are thrown automatically without the need of the try/catch block :
function getArtistTracks(artistName, 100){
searchDatabaseForTrack(artistName).then((songs) => {
// your previous code
})
.catch((err) => {
// handle error
});
});
Have searchDatabaseForTrack use Promise.all to return the loadedResults after all results have been gotten. Also, make sure not to implicitly create global variables as you're doing with thing. For example, try something like this:
async function searchDatabaseForTrack(query) {
const desiredResults = 100;
const trackPromises = Array.from(
({ length: Math.ceil(desiredResults / 50) }),
(_, i) => {
const offset = i * 50;
return databaseApi.searchTracks(query, { offset, limit: 50 });
}
);
const itemChunks = await Promise.all(trackPromises);
const loadedResults = itemChunks.reduce((a, { tracks: { items }}) => (
[...a, ...items]
), []);
return loadedResults;
};
and
searchDatabaseForTrack(artistName).then((loadedResults) => {
// do stuff with loadedResults
})
.catch((err) => {
console.log("ERROR!", err)
// handle error
});

Node js return function does not return data

Hi so i just made this function and I'm trying to make return the data but for some reason it does not return the data but the function is called as i tried console logging it when the function is executed it works fine but it just does return the data when i want to use the function
As you can see I'm using the function trying to put the returned data in a array
Thank you all.
a.push(methods.UserInfo(id));
Here's all the code I'm using
methods.UserDatas = ((client, json)=> {
let a = [];
if (json.Params.length > 0) {
json.Params.forEach((id)=>{
if (id) {
a.push(methods.UserInfo(id));
}
});
let cmd = {
'Cmd': 'GetUserInfo',
'userinfo': a
};
packet.send(client, JSON.stringify(cmd));
}
});
methods.UserInfo = ((id)=> {
sql.query('SELECT * FROM users WHERE ?',[{id:id}], (err, row)=>{
if (err) {
throw err;
} else {
if (row.length > 0) {
let playerData = {};
playerData.userid = row[0].id;
playerData.strGender = row[0].Gender;
playerData.Username = row[0].Username;
let items = {};
sql.query('SELECT * FROM users_items WHERE ? AND equipped=1',[{userid:id}],(err,rows) => {
if (err) {
throw err;
} else {
if (rows.length > 0) {
for (var i =0; i< rows.length; i++) {
let item = DBData.items.get(parseInt(rows[i].itemid));
items[rows[i].equipment] = {
'ItemID': item.id,
'File':item.File,
};
}
}
playerData["equipment"] = items;
return playerData;
}
});
}
}
});
});
An async function (like for example
sql.query('SQL', [{PARAM:VALUE}], (ERR, RESULT) => { CODE }
) does not return as a regular function: you could use promises, for example (see here).
And note, too, that a sync function calling an async function, does not synchronously return values coming from that async function...

Taking too much time when getting data from the database, using Promise.each in Bluebird

I know this is not a problem with Bluebird library, I am just wondering if I am using the Promises in the right way.
I have 200.000+ records on my MongoDB database, and trying to display those into a table in view. The time it takes now is 30-35 seconds, and I would like this number to decrease to 3-5 seconds if possible or smth like that.
For now this is the code I am using:
exports.report = function (req, res) {
var arrayReports = [];
Student.find({ status: role.STUDENT })
.populate('user')
.exec(function (err, students) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
}
Promise.each(students, function (student) {
return WorksnapsTimeEntry.find({ student: student._id })
.then(function (doc) {
var totalMinutes = 0;
var totalAvgLevelActivity = 0;
var counter = 0;
_.forEach(doc, function (item) {
if (item.timeEntries.duration_in_minutes) {
totalMinutes = totalMinutes + parseFloat(item.timeEntries.duration_in_minutes[0]);
}
if (item.timeEntries.activity_level) {
totalAvgLevelActivity = totalAvgLevelActivity + parseFloat(item.timeEntries.activity_level[0]);
counter++;
}
});
var obj = {};
obj.studentId = student._id;
obj.displayName = student.displayName;
obj.municipality = student.municipality;
obj.totalMinutes = totalMinutes;
obj.totalAvgLevelActivity = totalAvgLevelActivity / counter;
arrayReports.push(obj);
});
}).then(function () {
res.json(arrayReports);
});
});
};
Is there anyway that I dont need to use .each can I somehow use Promise.all or could you show me some code how would you do this so it would not take so much time to be executed faster.
Thanks

Categories

Resources