Why can´t I call a synchronous function inside a async promise? - javascript

I´m using express/ node, mysql and bluebird.
I´m performing three async database queries using Promise.all(). After these were performed I have to do some calculation.
If I do this I can call the function getDateRange() successfully, but this leads me to the problem, that I have trouble doing some error handling.
return Promise.all([departmentDatabase.getVacation(departmentID), departmentDatabase.countUser(departmentID), departmentDatabase.blockedDaysOfResponsible(departmentID)])
.then(departmentData => [departmentData, departmentData[0].map(entry => this.getDateRange(new Date(entry.dateFrom), new Date(entry.dateTo)))] )
.spread(function(departmentData, dateRange){
var mergedDateRange = [].concat.apply([], dateRange);
var counts = {};
mergedDateRange.forEach(function(x) { counts[x] = (counts[x] || 0)+1; }); //Zähle doppelt vorkommende Daten
var departmentMinStock = departmentData[0][0].departmentMinStock;
var blockedDays = [];
for (var p in counts) {
if( counts.hasOwnProperty(p) ) {
if( (parseInt(departmentData[1][0].amount) - parseInt(counts[p])) <= parseInt(departmentMinStock)) {
blockedDays.push(p);
}
}
}
return [{"vacationRequest": departmentData[0], "blockedDays": blockedDays}, departmentData[2]];
})
.catch(err => {
// ...do something with it...
// If you want to propagate it:
return Promise.reject(err);
// Or you can do:
// throw err;
});
In case that departmentData is undefined, which can happen quit often, I have no real option (that I know) to stop all following .then() calls and just return an empty array. I could return an empty array, but then I would have to do the check in every .then() is have.
So I thought, because I use Promise.all() I don´t really have following async operation, therefore I could just do all my logic inside the callback of .all().
But if I try to do it this way:
return Promise.all([departmentDatabase.getVacation(departmentID), departmentDatabase.countUser(departmentID), departmentDatabase.blockedDaysOfResponsible(departmentID)])
.then(function(data){
var d = [];
for(var i = 0; data[0].length > i; i++){
//console.log(this.getDateRange(new Date(data[0][0].dateFrom), new Date(data[0][0].dateTo)));
//console.log(data[0][i].dateFrom);
//console.log(data[0][i].dateTo);
var x = this.getDateRange(data[0][i].dateFrom, data[0][i].dateTo);
console.log(x);
// d.push(this.getDateRange(new Date(data[0][0].dateFrom), new Date(data[0][0].dateTo)));
}
return 1;
})
It results in an error TypeError: Cannot read property 'getDateRange' of undefined. But console.log(data[0][i].dateFrom); has a value I have checked that.
This is my getDateRange function:
getDateRange(startDate, stopDate) {
var dateArray = [];
var currentDate = moment(startDate);
while (currentDate <= stopDate) {
dateArray.push(moment(currentDate).format('YYYY-MM-DD'))
currentDate = moment(currentDate).add(1, 'days');
}
return dateArray;
}
Can someone exlpain me why this is happening? And best would be an example on how to do it right in this case?

Related

for loop is giving partial output in async function

I am getting partial output for the function call. Could anyone tell what I am doing wrong
Data in database in JSON format in MongoDB
DATA IN MONGODB
Function code
async function isTeamNameExists(department, teamID) {
var store = await new Promise(async (resolve, reject) => {
//database read function
db.readCollection(collection_name, (status, data) => {
if (status) {
var teamname = new SB.ListofNames();
teamname.listofboards = data;
var send = teamname;
console.log("send----->", send);
//checking for teamname exists?
for (var boardindex = 0; boardindex < send.listofboards.length; boardindex++) {
var tn = send.listofboards[boardindex];
if (tn.department == department && tn.teamId == teamId) {
resolve(tn);
} else {
resolve(null);
}
}
reject(null);
}
});
});
console.log("store---->", store); // the function return value =store
return (store);
//resolve(store); does not work
}
Function call 1
var output1 = isTeamNameExists(D1,11);
Result-> {department:D1 , teamId:11} // exists ->returns the particular json data of teamId=11
Function call 2
var output2= isTeamNameExists(D2,22);
Result-> null // it should not return null,but it should return {department:D2 , teamId:22}
Function call 3
var output3= isTeamNameExists(D78b,22000211);
Result-> null // it should return null. correct output
> So I am getting partial output. could you tell me what I am doing wrong? I am new to javascript, learning something everyday.
Your function is resolving the moment the first run in the for (var boardindex = 0; boardindex < send.listofboards.length; boardindex++) loop is done because of the else condition.
You want the loop to go through all the items and reject if no entry is found (which you are doing correctly.)
Remove the below part and you'll be good.
} else {
resolve(null);
//resolve(store); does not work will not work because isTeamNameExists function is not a promise.

JavaScript - How to hold for loop until you receive a function callback

Here is my for loop:
for (var d = startDate; d <= in30DaysDate; d.setDate(d.getDate() + 1)) {
var loopDay = new Date(d);
DoYourDaylyJob();
console.log("Day:" + loopDay);
}
What should i put in function DoYourDailyJob() to prevent the loop to go on the next day before it do it's "daily" job?
I hope i have described it well enough. Should i attach any kind of function callback at DoYourDailyJob, but if so how that is going to prevent the loop from proceeding until it receives response from the function ?
I'm not aware if this is possible. If it's possible can you show an example as an answer to this question ?
Just return a Promise from DoYourDailyJob, then its as simple as:
(async function() {
for (var d = startDate; d <= in30DaysDate; d.setDate(d.getDate() + 1)) {
var loopDay = new Date(d);
await DoYourDaylyJob();
console.log("Day:" + loopDay);
}
})()
Using a callback function, you replace the for with a recursive loop:
executeIteration(startDate);
function executeIteration(d) {
if (d <= in30DaysDate)
return;
var loopDay = new Date(d);
DoYourDaylyJob(function(valueFromDoYourDaylyJob) {
executeIteration(d.setDate(d.getDate() + 1)));
});
}
function DoYourDaylyJob(callback) {
// Do your dayly job
var valueToReturn = "foo";
callback(valueToReturn);
}
Just refactor it so its recursive:
var day = 0;
var loops = 10;
function DoYourDailyJob(){
var loopDay = new Date(d);
console.log("Day:" + loopDay);
if( day++ < loops ){
DoYourDailyJob();
}
}
DoYourDailyJob();
Short answer, you can't (at least without a super ugly blocking call that I can't recommend strongly enough that you avoid).
Instead, make it asynchronous.
If DoYourDailyJob() is something that is asynchronous, that you'll either want to make it:
accept a callback function which is called when complete
return a Promise that is resolved when complete
The Promise method tends to be more preferable these days:
function DoYourDailyJob() {
return new Promise((resolve, reject) => {
// do stuff
// call resolve() when done
// call reject() if there is an error
});
}
In your for loop, create an array of Dates you want to process:
const dates = [];
for (var d = startDate; d <= in30DaysDate; d.setDate(d.getDate() + 1)) {
dates.push(new Date(d));
}
With your list of Dates, you can then either run them all in parallel:
Promise.all(dates.map(d => DoYourDailyJob(d))
.then(() => console.log('all done'));
Or, if they need to be run one in a time (which may be the case since you don't pass in the date), you can essentially have a "queue" and a queue running function which will keep going until all of them are done:
const runNextDay = () => {
if (!dates.length) {
return Promise.resolve(); // all done
}
const day = dates.shift();
return DoYourDailyJob().then(() => console.log('loop date', day))
.then(runNextDay); // call the next day when its done
};
runNextDay()
.then(() => console.log('all done'));
All those comments are wrong, it is absolutely possible, in some environments.
If you have async await available, you can do it quite easily. Just make DoYourDailyJob(); an async function (returns a promise) and do
for () {
await DoYourDailyJob();
}
If you don't have async await available, comment that here and we can do something similar with raw promises.

Returning empty array in NodeJs using mongoose

I am trying to fill an array with records from a mongoDB database using mongoose. When I am trying to fill the records. It shows an empty array outside the function even though I am declaring the outside the function. Below is the code.
var saveMessageToVariable = function(){
var records = [];
var spark_ids = [];
var obj = new Object();
Message.find().distinct("spark_id").exec(function(err,data) {
data.forEach(function (id) {
if(id != null)
spark_ids.push(id);
});
// console.log(spark_ids.length);
spark_ids.forEach(function(spark_id){
Message.findOne({"spark_id":spark_id}).sort({"date":-1}).exec(function(err,data){
obj.spark_id = data.spark_id;
obj.meesage = data.message;
obj.date = data.date;
obj.message_id = data._id;
records.push(obj);
});
});
});
console.log(records);
}
When I run this, the log is showing an empty array. How do I resolve this issue?
It's an asynchronous call and as soon as data is fetched from database control shifts to next line and therefore prints the initial value, I would prefer you to use a callback like this:
function(spark_id,callback){
Message.findOne({"spark_id":spark_id}).sort({"date":-1}).exec(function(err,data){
obj.spark_id = data.spark_id;
obj.meesage = data.message;
obj.date = data.date;
obj.message_id = data._id;
callback(obj);
});
}
function(obj)
{
records.push(obj);
}
You two other approachs for this:
1) use try and catch block.
2) use async and await keyword.
Cheers!
I don't have much experience with moongoose, but according to the docs it supports promises since Version 4.
Then this should work:
//I assume you'll need this more often
function notNull(value){ return value != null; }
//returns a promise of the records-Array
var saveMessageToVariable = function(){
//returns a promise of a formated message
function getMessage( spark_id ){
return Message.findOne({ spark_id })
.sort({ date: -1 })
//.exec()
.then( formatMessage )
}
function formatMessage( msg ){
return {
spark_id: msg.spark_id,
message: msg.message,
date: msg.date,
message_id: msg._id
}
}
return Message.find()
.distinct("spark_id")
//.exec()
.then(function( ids ){
//waits for all findOnes to complete, then returns an Array
return Promise.all(
ids.filter( notNull ).map( getMessage )
));
}
I'm not sure, wether you need exec() in this code or not. You should check that.
//usage
saveMessageToVariable.then(function(records){
console.log(records);
})
btw. saveMessageToVariable doesn't reflect at all what this function does. You should choose a better name.

issue with pushing data into a new array while in a promise chain

I'm having trouble figuring out why my data is not being push into my new array, "results". newArr[0].mscd.g[i] is a list of several objects.
var axios = require('axios');
var moment = require('moment');
var _ = require('lodash');
var getData = function() {
return getNBASchedule().then(function(payload) {
return filterByMonth('January', payload);
}).then(function(result) {
return result
});
}
....
getData grabs the data from baseURL and returns a list of objects.
var getMonthlySchedule = function(data){
var results = [];
var newArr = data.slice(0, data.length);
for (var i = 0; i <= newArr[0].mscd.g.length; i++) {
if (newArr[0].mscd.g[i].v.tid === 1610612744 || newArr[0].mscd.g[i].h.tid === 1610612744) {
results.push(newArr[0].mscd.g[i]); <---- //does not seem to work
// however if I were to console.log(newArr[0].mscd.g[i],
// I would see the list of objects)
}
}
return results; <-- //when i console at this point here, it is blank
};
var getSchedule = function () {
return getData().then(function(pl) {
return getMonthlySchedule(pl)
})
};
var monthlyResults = function() {
return getSchedule().then(function(r) {
console.log("result", r)
return r
});
};
monthlyResults();
You don't know when getSchedule() is done unless you use a .then() handler on it.
getSchedule().then(function(data) {
// in here results are valid
});
// here results are not yet valid
You are probably trying to look at your higher scoped results BEFORE the async operation has finished. You HAVE to use .then() so you know when the operation is done and the data is valid.
Your code should simplify as follows :
var getData = function() {
return getNBASchedule().then(function(payload) {
return filterByMonth('January', payload);
});
}
var getMonthlySchedule = function(data) {
return data[0].mscd.g.filter(function(item) {
return item.v.tid === 1610612744 || item.h.tid === 1610612744;
});
};
var monthlyResults = function() {
return getData()
.then(getMonthlySchedule)
.then(function(r) {
console.log('result', r);
return r;
});
};
monthlyResults();
This may fix the problem. If not, then :
Check the filter test. Maybe those .tid properties are String, not Number?
Check that data[0].mscd.g is the right thing to filter.

Fill an array with dates when $http/rest requests did not return a response

I want to perform an http/rest request for 30 days and fill an array with the dates that did not return a response. What is a good way to do this? I don't know a good pattern for it and find it confusing because of the asynchronous nature of http/rest requests.
The code below works but it stores recent reported. I want to store recent unreported.
$scope.recentReported = [];
$scope.getRecentReported = function () {
var dateToday = new Date();
for (i = 0; i < $scope.recentDateLength; i++) {
var dateThen = moment(dateToday).add(-i, 'days');
RestRequestsSrvc.getRevenue(dateThen)
.then(
function (response) {
if (response) {
$scope.recentReported.push(response.date);
}
}
);
}
};
To get this working you need to use $q and chain your $http calls.
First get an array with the dates:
var dates = [];
for (i = 0; i < $scope.recentDateLength; i++) {
dates.push(moment(dateToday).add(-i, 'days'));
}
Then chain your calls like so:
$q.all(dates.map(function(dateThen){
return RestRequestsSrvc.getRevenue(dateThen);
})).then(function(results){
//results now contains some null values for the responses that did not return any value
results.forEach(function(result, resultIndex){
if(!result){
$scope.recentUnReported.push(//your fill up here);
};
});
});

Categories

Resources