Translating a while loop to a promise - node.js

I am trying to create a report which is stretching the limits of my understanding of both Node and MySQL.
I have already established that the annual date information I need will need to be called for each year I want to display. I now have a query that can return the correct data that I need, I just need to repeat that within a Node environment.
Coming from a Delphi background the following code would provide me with the data I need
LoadTurnover = function (req, reply) {
const queryDay = "-04-06";
const maxDate = new Date();
let queryYear = 2000;
let qd = new Date(queryYear + queryDay);
let dateArray = [];
while (qd < maxDate) {
// Get the data from the server
let data = getData(sql);
//
let turnoverObj = {};
turnoverObj.date = qd;
turnoverObj.Employees = data[0][0].Employees;
turnoverObj.Leavers = data[1][0].Leavers;
// Add current year data to our result set
dateArray.push(turnoverObj);
// Setup the next year condition
queryYear ++;
qd = new Date(queryYear + queryDay);
}
};
I need to be able send a Promise to the DB server (getData) and populate the turnoverObj and dateArray appropriatly. This needs to repeat until qd is greater than today's date.

You can use .map() function of Bluebird promise:
var Promise = require('bluebird);
let dates = [];
let dateArray = [];
while (qd < maxDate) {
dates.push(qd);
queryYear++;
qd = new Date(queryYear + queryDay);
}
Promise.map(dates, function(singleDate){
return getData(sql).then(function(data){
let turnoverObj = {};
turnoverObj.date = singleDate;
turnoverObj.Employees = data[0][0].Employees;
turnoverObj.Leavers = data[1][0].Leavers;
dateArray.push(turnoverObj);
return;
});
}).then(function(finalResult){
console.log(dateArray);
});
Hope this helps somehow.

Related

How can node.js process large-capacity Excel files separately?

https://github.com/exceljs/exceljs
There is a problem in creating applications through electron and Exceljs.
I want to read the Excel file and show it to user in the html table. The problem is that it deals with xlsx files with a very large number of rows.
So I want to show the progress to the user as follows.
ex) 5/10000. (Read row / Total number of rows)
Also, if possible, I would like to return the data in the form of json or html whenever I read it. (If this is impossible, I'd like to indicate that it's loading until it's finished)
const filePath = "/Users/caglia/Documents/test.xlsx"
const ExcelJS = require('exceljs')
async function loadExcelFile(filePath) {
let start = new Date();
console.log('start :', Date.now())
const sheetData = []
const workbook = new ExcelJS.Workbook()
await workbook.xlsx.readFile(filePath)
const worksheet = workbook.worksheets[0] // first sheet
const options = {
includeEmpty: true
}
await worksheet.eachRow(options, (row, rowNum) => {
sheetData[rowNum] = []
//console.log('rowNum', rowNum)
row.eachCell(options, (cell, cellNum) => {
sheetData[rowNum][cellNum] = {
value: cell.value
}
})
})
let end = new Date();
console.log('finish ', Date.now(), 'timediff', end - start);
}
loadExcelFile(filePath)

Call multiple API in for loop and get one by one response using node js

I have to call multiple API using promise all in for loop but I am getting a response in unsequential format. e.g. I have 3 rows but getting a response in 1,2,3 sequence. First getting the first-row response than 3rd row and then getting 2nd row but I need to get a response in a sequential format like (1 row,2 rows,3 rows).
result = '{"error_code":0,"err_desc":null,"data":[{"":"","name":"OTT, KATHRYN M","address":"3110 Horseshoe Trl, Glenmoore, PA","email":"","phone1":"(410) 599-2212","phone2":"(610) 827-9107","phone3":"(610) 308-4566","phone4":"(610) 506-1121","phone5":"(610) 469-0737","phone6":"(610) 942-4347","phone7":"323-7898","phone8":"(814) 371-6133","phone9":""},{"":"","name":"BELTRANTE, SUSAN E","address":"3 Rhoads Ave, Moorestown, NJ\"","email":"SUSAN.BELTRANTE#AOL.COM, JOE.BARGER#YAHOO.COM,","phone1":"(856) 266-0381","phone2":"(856) 273-0869","phone3":"(609) 266-0381","phone4":"(856) 235-3933","phone5":"","phone6":"","phone7":"","phone8":"","phone9":""},{"":"","name":"Manish","address":"4895 E American Beauty Dr, Tucson, AZ 85756","email":"abc#gmail.com","phone1":"(857) 266-0381","phone2":"(857) 273-0869","phone3":"(610) 266-0381","phone4":"(857) 235-3933","phone5":"","phone6":"","phone7":"","phone8":"","phone9":""}]}';
var i;
for (i = 0; i < result.length; i++)
//for (i = 0; i <= 0; i++)
{
var phone = result[i].phone9;
var name = result[i].name;
var address = result[i].address;
var email = result[i].email;
var phone1 = result[i].phone1;
var phone2 = result[i].phone2;
var phone3 = result[i].phone3;
var phone4 = result[i].phone4;
var phone5 = result[i].phone5;
var phone6 = result[i].phone6;
var phone7 = result[i].phone7;
var phone8 = result[i].phone8;
var addressinfo = address.split(',');
var street = addressinfo[0];
var city = addressinfo[1];
var state = addressinfo[2];
var zip = addressinfo[3];
Promise.all([
fetch('https://backend.mioym.properties/api/247/eppraisal?street='+street+'&zip='+zip),
fetch('https://backend.mioym.properties/api/247/zillow?street='+street+'&zip='+zip),
fetch('https://backend.mioym.properties/api/247/pennymac?address='+address),
fetch('https://backend.mioym.properties/api/247/chase?address='+address),
fetch('https://backend.mioym.properties/api/247/realtor?address='+address)
]).then(function (responses) {
// Get a JSON object from each of the responses
return Promise.all(responses.map(function (response) {
console.log("here"+i);
//console.log(response.json());
console.log(response.url);
return response.json();
}));
}).then(function (data) {
console.log("success"+i);
// Log the data to the console
// You would do something with both sets of data here
console.log(data);
}).catch(function (error) {
console.log("error"+i);
// if there's an error, log it
console.log(error);
});
}
So please anyone suggest me solution.
The second Promise.all inside your then block is not necessary, as responses will already contain the resolved values. Note that Promise.all processes the requests in parallel but the resolved responses will be in order. So you can simply do:
Promise.all([
fetch('https://backend.mioym.properties/api/247/eppraisal?street=' + street + '&zip=' + zip),
fetch('https://backend.mioym.properties/api/247/zillow?street=' + street + '&zip=' + zip),
fetch('https://backend.mioym.properties/api/247/pennymac?address=' + address),
fetch('https://backend.mioym.properties/api/247/chase?address=' + address),
fetch('https://backend.mioym.properties/api/247/realtor?address=' + address)
]).then(function (responses) {
// Get a JSON object from each of the responses
return responses.map(function (response) {
console.log(response.url);
return response.json();
});
});

copy data from one node1 child to another node2 child with using firebase cloud functions

Below is my code of cloud functions I am trying to copy the data from users node to challenge node
exports.copyChallengeDate = functions.database.ref(`Users/{pushId}/DateChallengeAccept`).onCreate((snapshot, context)=>{
var DateChallengeAccept = snapshot.val();
console.log('Challenge', context.params.pushId, DateChallengeAccept);
var challenge = DateChallengeAccept;
return admin.database().ref('Challenge/' + context.params.pushId).child('DateChallengeAccept').set(challenge);
});
But the thing is when I am trying to copy the date from user table to challenge node it's only occur in QS5h99YxS0ZGpT42fpUFvzOdpTi1
D uid same as of Users node. I want to copy it in both uid's(QS5h99YxS0ZGpT42fpUFvzOdpTi1, 7aH9Ag8414VzM0n7P6ur4LvcepI2)
D present in challenge node. I was stuck in it from last two days please help me out
Update following you remark that DateChallengeAccept is not initialized when you create the record. You should use another method, like onUpdate() (or onWrite()) and not onCreate(), as you do in your question. Do as follows:
exports.copyChallengeDate = functions.database.ref(`Users/{pushId}`).onUpdate((change, context) =>
const DateChallengeAccept = change.after.val().DateChallengeAccept;
if ( DateChallengeAccept === undefined) {
return false;
}
const referredBy = change.after.val().referredBy;
console.log('Challenge', context.params.pushId, DateChallengeAccept);
const dateChallengeAcceptObj = {
"DateChallengeAccept": DateChallengeAccept
};
let updates = {};
updates['Challenge/' + context.params.pushId + '/DateChallengeAccept'] = dateChallengeAcceptObj;
updates['Challenge/' + referredBy + '/DateChallengeAccept'] = dateChallengeAcceptObj;
return admin.database().ref().update(updates);
});
You need to get the referredId value in your Function, because it is this data item that holds the ID of the other user (i.e. 7aH9Ag8414VzM0n7P6ur4LvcepI2). So you have to trigger the event on the parent node, not at the DateChallengeAccept node.
Then you have to use the update() method to write simultaneously to two nodes, see the doc here: https://firebase.google.com/docs/database/web/read-and-write#update_specific_fields
So you should do as follows:
exports.copyChallengeDate = functions.database.ref(`Users/{pushId}`).onCreate((snapshot, context)=>{
const DateChallengeAccept = snapshot.val().DateChallengeAccept;
if ( DateChallengeAccept === undefined) {
return false;
}
const referredBy = snapshot.val().referredBy;
console.log('Challenge', context.params.pushId, DateChallengeAccept);
const dateChallengeAcceptObj = {
"DateChallengeAccept": DateChallengeAccept
};
let updates = {};
updates['Challenge/' + context.params.pushId + '/DateChallengeAccept'] = dateChallengeAcceptObj;
updates['Challenge/' + referredBy + '/DateChallengeAccept'] = dateChallengeAcceptObj;
return admin.database().ref().update(updates);
});

Nodejs run promises sequentially

Dears ,
How can i run promises in nodejs sequentially , in the following example am looping through array of hours then for each fetched hour get result from the database , the issue here : am getting results but i want it sequentially same order that i got hours .
angular.forEach(SharedVar.getCategories(), function (h) {
t = h.split('-', 2);
t = t[0];
RESTApi.getAnswerdCallsByHour(t).then(function (answerdTotal) {
$scope.answerdCallsTotalByHour.push(answerdTotal);
var d = SharedVar.getDataS();
d[count] = answerdTotal;
SharedVar.setDataS(d);
count++;
});
});
Thanks ,
var promise = Promise.resolve(); // make an empty promise in the way you do it with your promise library
angular.forEach(SharedVar.getCategories(), function (h) {
promise.then(function() {
return RESTApi.getAnswerdCallsByHour(t).then(function (answerdTotal) {});
});
});
The way to do it sequently would be to do one Request and do the next request inside the promise.
I think the better approach by far is to extend your SharedVar.setDataS(d) function in a way, that it does not depend on getting the data sequentially. Like having a SharedVar.setDataS(d, index) and using the config var in your $http.get (or whatever) functioncall inside your RESTApi to promote that index all the way to the promise.
If your RESTApi looks like this:
var RESTApi = {
getAnswerdCallsByHour : function(hour) {
var url = "bla.com/myservice?hour=" + hour;
return $http.get(url).data;
}
// Some other Methods...
Then you need a way to pass something to "reorder" your Data when it arrives asynchronously, this could be a index you count up or in your case maybe the hour Variable:
var RESTApi = {
getAnswerdCallsByHour : function(hour) {
var url = "bla.com/myservice?hour=" + hour;
var config = [];
config.hour = hour;
return $http.get(url, config); // Return the promise not just data or a specific field
}
// Some other Methods...
Now when your promise is fullfiled you can access your "hour" Variable like so:
var d = SharedVar.getDataS();
d[promise.config.hour] = promise.data;
SharedVar.setDataS(d);
Now you know what piece of data correlates to which request and you do not need to recieve Data in order. The last piece only works properly when hours runs sequential from 0 to 23, if that isn't the case you need to:
var RESTApi = {
getAnswerdCallsByHour : function(hour, index) {
var url = "bla.com/myservice?hour=" + hour;
var config = [];
config.index = index;
return $http.get(url, config);
}
// Some other Methods...
...
...
var d = SharedVar.getDataS();
d[promise.config.index] = promise.data;
SharedVar.setDataS(d);
Safari's answer is how I typically handle this. (Sorry, I don't have enough rep to comment yet...) You were experiencing problems with it because the example provided does not capture and use the new promise in subsequent loops. See my comments on the slightly modified version here:
var promise = Promise.resolve();
angular.forEach(SharedVar.getCategories(), function (h) {
t = h.split('-', 2);
t = t[0];
// You must capture the new promise here; the next loop will wait
// for the promise returned from getAnswerdCallsByHour to resolve.
promise = promise.then(function() {
// Halt downstream promises until this returned promises finishes
return RESTApi.getAnswerdCallsByHour(t).then(function (answerdTotal) {
$scope.answerdCallsTotalByHour.push(answerdTotal);
var d = SharedVar.getDataS();
d[count] = answerdTotal;
SharedVar.setDataS(d);
count++;
});
});
});

Why is my for-loop only counting to one object in the JSON-response?

I'm trying to make a map with map-annotations which are being generated from a REST-JSON response. I've succeeded with making one, the JSON response contains two objects. Why are only one printed out?
I'm using RestSharp and Xamarin.iOS.
Here's a Gist-clone of the original respones
The function that grabs the data to later-on make map-annotations on our map:
Action getAllMarkers = () => {
var client = new RestClient("http://www.example.com/");
var request = new RestRequest(String.Format("api/?function=searchByName&key=&name=Sundsvall"));
client.ExecuteAsync (request, response => {
JsonValue data = JsonValue.Parse(response.Content);
for (var i = 0; i < data.Count; i++){
Double lat = data["result"][i]["lat"];
Double lng = data["result"][i]["lng"];
String name = data["result"][i]["title"];
String adress = data["result"][i]["adress"];
var store = new BasicMapAnnotation (new CLLocationCoordinate2D(lat, lng), name, adress);
Console.WriteLine(response.Content);
InvokeOnMainThread ( () => {
// manipulate UI controls
map.AddAnnotation(store);
});
}
});
};
getAllMarkers();
data.Count is 1, because there is one top level "result" node in your json. Use data["result"].Count instead.
your result is main array in which rest of the data is so use:data["result"].Count instead of data.Count

Resources