Trouble with Node JS promises - javascript

I cannot find a solution to why this function returns before my message array is updated with the necessary values.
var calculateDistance = function (message, cLongitude, cLatitude, cSessionID) {
return new Promise(function (resolve, reject) {
distance.key = options.apiKey;
distance.units('metric');
var origins = [];
origins.push(cLatitude + ',' + cLongitude);
message.forEach(function (obj) {
obj.sessionId = cSessionID;
var destinations = [];
destinations.push(obj.geoLocation.latitude + ',' + obj.geoLocation.longitude);
distance.matrix(origins, destinations, function (err, distances) {
if (err) {
return console.log(err);
}
if (!distances) {
return console.log('no distances');
}
if (distances.status == 'OK') {
for (var i = 0; i < origins.length; i++) {
for (var j = 0; j < destinations.length; j++) {
var origin = distances.origin_addresses[i];
var destination = distances.destination_addresses[j];
if (distances.rows[0].elements[j].status == 'OK') {
var distance = distances.rows[i].elements[j].distance.text;
console.log('Distance from ' + origin + ' to ' + destination + ' is ' + distance);
obj.distance = distance;
} else {
console.log(destination + ' is not reachable by land from ' + origin);
obj.distance = 'N/A';
}
}
}
}
});
});
return resolve(message);
});
}
Could someone point out to me what i am doing wrong here.
Regards
Jimmy

var async = require('async');
var calculateDistance = function (message, cLongitude, cLatitude, cSessionID) {
return new Promise(function (resolve, reject) {
distance.key = options.apiKey;
distance.units('metric');
var origins = [];
origins.push(cLatitude + ',' + cLongitude);
async.each(message, function(obj, callback) {
obj.sessionId = cSessionID;
var destinations = [];
destinations.push(obj.geoLocation.latitude + ',' + obj.geoLocation.longitude);
distance.matrix(origins, destinations, function (err, distances) {
if (err) {
callback(err);
}
if (!distances) {
callback('no distances');
}
if (distances.status == 'OK') {
for (var i = 0; i < origins.length; i++) {
for (var j = 0; j < destinations.length; j++) {
var origin = distances.origin_addresses[i];
var destination = distances.destination_addresses[j];
if (distances.rows[0].elements[j].status == 'OK') {
var distance = distances.rows[i].elements[j].distance.text;
console.log('Distance from ' + origin + ' to ' + destination + ' is ' + distance);
obj.distance = distance;
} else {
console.log(destination + ' is not reachable by land from ' + origin);
obj.distance = 'N/A';
}
}
}
callback(null);
}
});
},function(err){
if(err){
return reject(err);
}else{
return resolve(message);
}
});
});
};

This is happening because your distance.matrix(origins, destinations, callback )is asynchronous . In above code distance.matrix method is getting pushed to event loop and continues it's execution and before that method callbacks gets executed resolve(message) is returned .

You need to read up on promises. It looks to me as if you are thinking of promises as magical way to set up a callback. "Magic" tends to mean "something I don't need to understand." In this case that's not true.
That executor function of yours (that is the function which begins with 'function(resolve,reject)') should set up one asynchronous request. If, as is normal, the request has a callback you put the 'resolve' and 'reject' in the callback. The result will be a promise object which has methods 'then' and 'catch' where your post-request processing goes.
Since you want to fill up a matrix with the results of a lot of async requests, you will need to read about 'Promise.all' so you can react when all of them have resolved.

Related

Javascript fetch exception causes dead stop

I have the following code:
function makeid(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() *
charactersLength));
};
return result;
};
var instance = "{{ user }}" + makeid(16);
var checksum = "First Request Not recieved";
console.log(instance);
function downloadPlay(){
console.log("\ndownloadPlay - Begin\n")
try{
fetch("/file?instance=" + instance + "&checksum=" + checksum)
.then(function(resp) {
resp.headers.forEach(
function(val, key) {
// console.log("key, val: " + key + ", " + val);
if(key == "checksum"){
console.log("checksum: " + val);
checksum = val;
};
}
);
}
)
.then(file => {
var audio = new Audio("/file?instance=" + instance + "&checksum=" + checksum);
console.log("Done");
audio.addEventListener('ended', (event) => {
delete audio;
downloadPlay();
});
audio.play();
}
)
} catch (error) {
console.log("Something went wrong, Retrying: " + error);
}
console.log("downloadPlay - Complete\n")
};
downloadPlay();
This works perfectly when the promise succeeds. However when it fails(such as when the client device switches networks, i.e. wifi to data or just different access points on the same wifi network) it stops dead and never resumes no matter how many while loops, extra recursion points or try and catch statements I use. The best I could do so far is get it to play ever increasing numbers of the audio mostly in sync with each other and I just dont understand why. It seems I have a general lack of understanding of how this promise thing actually functions, but no matter how many tutorials I read/watch my lack of understanding seems to remain unchanged.
Heres the code that somewhat worked if that helps:
function makeid(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() *
charactersLength));
};
return result;
};
var instance = "{{ user }}" + makeid(16);
var checksum = "First Request Not recieved";
console.log(instance);
function downloadPlay(){
console.log("\ndownloadPlay - Begin\n")
try{
console.log('fetching')
fetch("/file?instance=" + instance + "&checksum=" + checksum)
.then(function(resp) {
resp.headers.forEach(
function(val, key) {
// console.log("key, val: " + key + ", " + val);
if(key == "checksum"){
console.log("checksum: " + val);
checksum = val;
};
}
);
}
).catch(function(error) {
console.log('request failed', error)
console.log('retrying')
downloadPlay();
return;
})
.then(file => {
var audio = new Audio("/file?instance=" + instance + "&checksum=" + checksum);
console.log("Done");
audio.addEventListener('ended', (event) => {
delete audio;
downloadPlay();
});
audio.play();
}
)
} catch (error) {
console.log("Something went wrong, Retrying: " + error);
}
console.log("downloadPlay - Complete\n")
};
downloadPlay();
Any solution or very simple explanation on what im doing wrong would be much appreciated
Thanks in advance :)
You can do something like this
Just remove the comment and use your original fetching function
You can't use try catch with promises unless you use async await
const fakeChecking = Promise.resolve({headers: {checksum: 'aaaa'}})
const errorChecking = Promise.reject('error')
function downloadPlay(fetching) {
console.log("\ndownloadPlay - Begin\n")
console.log('fetching')
fetching
.then((resp) => resp.headers.checksum)
.then(checksum => {
/*var audio = new Audio("/file?instance=" + instance + "&checksum=" + checksum);
console.log("Done");
/*audio.addEventListener('ended', (event) => {
delete audio;
downloadPlay();
console.log("downloadPlay - Complete\n")
});
audio.play();*/
console.log("downloadPlay - Complete\n")
})
.catch(function(error) {
console.log('request failed', error)
console.log('retrying')
downloadPlay(fakeChecking);
})
};
downloadPlay(errorChecking);

ReplaceAll causing issues in array.reduce

I am still pretty new to this, so forgive me if I dont' say this correctly. We have an array.reduce that calls a method with a returning promise that iterates through a list of files and post results to the db. Everything was working great, until it ran into a field that had an apostrophe in it and then the db insert fails. This is the field value. 'Expected 100002822' to be 100002822.'
I tried adding a replaceAll on the field and now get an error in the array.reduce.
Here is the .reduce
console.log('Found test results in ' + matches.length + ' files. Parsing and posting to the database now...');
var startTime = moment();
var parser = new Parser();
matches.reduce(function (p, val) {
return p.then(function () {
return parser.parseResults(val);
});
}, Promise.resolve()).then(function (finalResult) {
var endTime = moment();
var testDuration = moment.duration(endTime.diff(startTime));
console.log(chalk.blue('*** File parsing time: ' + testDuration.humanize() + ' ***'));
if (finalResult.insertSuccess == matches.length) {
var publishOut = {
totalFiles: matches.length,
totalTests: 0,
totalTestsSuccess: 0,
totalTestsFailed: 0
}
publishOut.totalTests += finalResult.totalTests;
publishOut.totalTestsSuccess += finalResult.testPassedCount;
publishOut.totalTestsFailed += finalResult.testFailedCount;
console.log(`Successfully inserted ${finalResult.insertSuccess} of ${publishOut.totalTests} test results.`);
// for (var i = 0; i < matches.length; i++) {
// var currentFile = `./testing/results/${matches[i]}`;
// fs.unlinkSync(currentFile);
// }
resolve(publishOut);
} else {
reject('Only ' + finalResult.insertSuccess + ' of ' + matches.length + ' successfully posted to the database');
}
}, function (err) {
reject('error in reduce', err);
});
I have tried several different ways of using the replaceAll with the same failure. It hits this code from the array.reduce
}, function (err) {
reject('error in reduce', err);
});
And this is the called method. The added code causing the failure in the .reduce is this Message = expectation.message.replaceAll("'", "");
protractorParser.prototype.parseResults = function (fileName) {
return new Promise((resolve, reject) => {
//console.log('In parseresults', fileName);
var currentFile = './testing/results/' + fileName
json.readFile(currentFile, function (err, obj) {
if (err != null) {
console.log('error reading file', err);
reject(err);
} else {
resolve(obj);
}
});
}).then(function (obj) {
var results = [];
for (var suite in obj) {
var specs = obj[suite].specs;
for (let i = 0; i < specs.length; i++) {
const assert = specs[i];
const tcR = /TC[\d]+/;
const tc = assert.description.match(tcR);
let Passed = 1;
let Message = '';
let Stack = '';
testResults.totalTests++;
if (assert.failedExpectations.length) {
const expectation = assert.failedExpectations[assert.failedExpectations.length - 1];
Passed = 0;
Message = expectation.message.replaceAll("'", "");
Stack = expectation.stack.split('\n')[1].trim();
testResults.testFailedCount++
} else {
testResults.testPassedCount++
}
if (tc != null) {
const time = moment().utcOffset(config.get('settings.timeOffset')).format('YYYY-MM-DDTHH:mm:ss');
const promise = utility.TestDataManager.insertAutomationResults(tc[0], assert.description, Passed, process.env.testBuild, 'P', Message, Stack, 0, time, '');
results.push(promise.then(() => {
//fs.unlinkSync(currentFile);
testResults.insertSuccess++;
//console.log('insertSuccess', testResults.insertSuccess);
},
err => { console.log('… failed', err); throw err; }
));
} else {
console.log('no test case found for test: ' + assert.description + ' -- skipping');
// I don't think you want to `throw err` here, right?
}
}
}
return Promise.all(results).then(() => testResults);
});
};

Promises.all() is not waiting for the resolution of the promises

I have a node.js code in which I want to wait for all my sql queries to complete
and then process the next part.
Here I am returning the promises and storing in the array.
After all the promises are returned I want to do some calculation.
But it seems that Promises.all() is not waiting for the promises to get resolve and gives me 0 in the calculation stage ?
Can someone help me with this, where I am doing the mistake ?
var results= [];
var salaries = 0;
var numberOfWorking = 0;
var numberOfNotFound = 0;
var numberOfFound = 0;
var averageSalary = 0;
var promises = [];
for (grad in graduates) {
promises.push(function () {
return new Promise(function (resolve, reject) {
var entree = graduates[grad];
console.log("entree:", JSON.stringify(entree));
var query = "SELECT Salary FROM employment WHERE FirstName='" + entree['firstName'] + "' AND MiddleName='" + entree['middleName'] + "'" +
"AND LastName='" + entree['lastName'] + "' AND DOB='" + entree['birth'] + "'";
connection.query(query, function (err, rows, fields) {
if (!err) {
if (rows.length == 0) {
numberOfNotFound = numberOfNotFound + 1;
}
else {
var Salary = parseInt(rows[0].Salary, 10)
console.log("Salary", Salary);
numberOfFound = numberOfFound + 1;
salaries += Salary;
if (Salary > 0) {
numberOfWorking = numberOfWorking + 1;
}
console.log("salaries", salaries);
console.log("numberOfFound", numberOfFound);
console.log("numberOfWorking", numberOfWorking);
resolve(true);
}
}
else {
console.log(req.body);
console.log("ERROR:", err);
reject(err);
}
});
});
});
}
Promise.all(promises).then(function (dataArr) {
if (numberOfFound > 0) {
averageSalary = salaries / numberOfFound;
}
else {
averageSalary = 0;
}
console.log("salaries", salaries);
console.log("averageSalary", averageSalary);
console.log("numberOfFound", numberOfFound);
console.log("numberOfWorking", numberOfWorking);
var tosendOnePkg = {};
tosendOnePkg = {
"packageID": pId,
"numberOfPersons": numberOfFound + numberOfNotFound,
"numberOfWorkingPersons": numberOfWorking,
"notFoundPersons": numberOfNotFound,
"averageSalary": averageSalary
}
tosendOnePkg['packageID'] = package;
results.push(tosendOnePkg);
});
The issue is that you're adding functions to the promise array, see line 9 of your example:
promises.push(function () {
return new Promise(function (resolve, reject) {
...
}
});
Instead, you should add the promise itself:
promises.push(new Promise(function (resolve, reject) {
...
});

Node.js Function Flow

When I get a request, I want it to generate a 4-character code, then check if it already exists in the database. If it does, then generate a new code. If not, add it and move on. This is what I have so far:
var code = "";
var codeFree = false;
while (! codeFree) {
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var code = "";
for (var i = 0; i < 4; i++) {
var rand = Math.floor(Math.random() * chars.length);
console.log(rand);
code += chars.charAt(rand);
}
console.log("Code: %s generated.", code);
client.execute("select * from codes where code=" + code, function(err, result) {
if (! err) {
if (result.rows.length > 0) {
codeFree = false;
} else {
codeFree = true;
}
} else {
console.log('DB ERR: %s', err);
}
console.log(codeFree);
});
console.log('here');
}
This does not do nearly what I want it to do. How can I handle something like this?
You are doing an async task.
When you have an asyncronous task inside your procedure, you need to have a callback function which is going to be called with the desired value as its argument.
When you found the free code, you call the function and passing the code as its argument, otherwise, you call the getFreeCode function again and passing the same callback to it. Although you might consider cases when an error happens. If your the db call fails, your callback would never get called. It is better to use a throw/catch mechanism or passing another argument for error to your callback.
You can achieve what you need to do by doing it this way:
function getFreeCode(callback) {
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var code = "";
for (var i = 0; i < 4; i++) {
var rand = Math.floor(Math.random() * chars.length);
console.log(rand);
code += chars.charAt(rand);
}
console.log("Code: %s generated.", code);
client.execute("select * from codes where code="+code, function(err, result) {
if(!err) {
if(result.rows.length > 0) {
getFreeCode(callback);
} else {
callback(code);
}
}else {
console.log('DB ERR: %s', err);
}
console.log(codeFree);
});
console.log('here');
}
// in your main:
getFreeCode(function (code) {
console.log(' this code was free: ' + code)
})
I recommend you look into two alternatives to help deal with asynchronous code.
node generator functions using the 'yield' keyword
promises
Using generators requires running a recent version of node with the --harmony flag. The reason I recommend generators is because you can write code that flows the way you expect.
var x = yield asyncFunction();
console.log('x = ' + x);
The previous code will get the value of x before logging x.
Without yielding the console.log would write out x before the async function was finished getting the value for x.
Your code could look like this with generators:
var client = {
execute: function (query) {
var timesRan = 0;
var result = [];
return function () {
return setTimeout(function () {
result = ++timesRan < 4 ? ['length_will_be_1'] : [];
return result;
},1);
};
}
};
function* checkCode () {
var code;
var codeFree = false;
while(!codeFree) {
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
code = "";
for (var i = 0; i < 4; i++) {
var rand = Math.floor(Math.random() * chars.length);
console.log(rand);
code += chars.charAt(rand);
}
console.log("Code: %s generated.", code);
try {
var result = yield client.execute("select * from codes where code="+code);
codeFree = result.rows.length > 0 ? false : true;
}catch(e) {
console.log('DB ERR: %s', err);
} finally {
console.log(codeFree);
}
console.log('here');
}
}
checkCode().next();
You would leave off the client object. I only added that to make a working example that fakes an async call.
If you have to use an older version of node or do not like the yield syntax then promises could be a worthy option.
There are many promise libraries. The reason I recommend promises is that you can write code that flows the way you expect:
asyncGetX()
.then(function (x) {
console.log('x: ' + x);
});
The previous code will get the value of x before logging x.
It also lets you chain async functions and runs them in order:
asyncFunction1()
.then(function (result) {
return asyncFunction2(result)
})
.then(function (x) { /* <-- x is the return value from asyncFunction2 which used the result value of asyncFunction1 */
console.log('x: ' + x);
});
Your code could look like this with the 'q' promise library:
var Q = require('q');
var client = {
timesRan: 0,
execute: function (query, callback) {
var self = this;
var result = {};
setTimeout(function () {
console.log('self.timesRan: ' + self.timesRan);
result.rows = ++self.timesRan < 4 ? ['length = 1'] : [];
callback(null, result);
},1);
}
};
function checkCode () {
var deferred = Q.defer();
var codeFree = false;
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var code = "";
for (var i = 0; i < 4; i++) {
var rand = Math.floor(Math.random() * chars.length);
console.log('rand: %s', rand);
code += chars.charAt(rand);
}
console.log("Code: %s generated.", code);
client.execute("select * from codes where code="+code, function(err, result) {
console.log('err: '+err+', result: ' + JSON.stringify(result));
console.log('result.rows.length: ' + result.rows.length);
if(!err) {
if(result.rows.length > 0) {
codeFree = false;
console.log('result.rows: %s, codeFree: %s', result.rows, codeFree);
checkCode();
} else {
codeFree = true;
console.log('line 36: codeFree: ' + codeFree);
deferred.resolve(code);
}
}else {
console.log('DB ERR: %s', err);
deferred.reject(err);
}
console.log(codeFree);
});
console.log('waiting for promise');
return deferred.promise;
}
checkCode()
.then(function (code) {
console.log('success with code: ' + code);
})
.fail(function(err) {
console.log('failure, err: ' + err);
});
Also omit the client object here. I only added that to make a working example that fakes an async call.
Promises and generators definitely take some time to get used to. It's worth it because they make the code a lot easier to follow in the end than code written with nested callbacks.

async callback for loop response out of order

I do a async call in a for loop and i know that the response is coming async but how can i get my response always in the same order. Here's my code:
setInterval(function () {
callback = function (response)
{
var temp2 = '';
var str = "";
test = [];
console.log('STATUS: ' + response.statusCode);
response.setEncoding('utf8');
response.on('data', function (chunk)
{
str += chunk;
});
response.on('end', function ()
{
console.log("end found");
temp2 = JSON.parse(str);
for (var i in temp2['build'])
{
test.push(temp2['build'][i]['id']);
var req3 = http.request({
host: host, // here only the domain name
auth: auth,
port: 8111,
path: '/httpAuth/app/rest/builds/id:' + test[i] + '/statistics/', // the rest of the url with parameters if needed
method: 'GET', // do GET
headers: { "Accept": "application/json" }
}, callback2);
req3.end();
}
});
}
var req4 = http.request(options4, callback);
req4.end();
callback2 = function (response) {
//console.log('STATUS: ' + response.statusCode);
//console.log('HEADERS: ' + JSON.stringify(response.headers));
response.setEncoding('utf8');
var str2 = "";
response.on('data', function (chunk) {
str2 += chunk;
});
response.on('end', function () {
points.push(parseInt(JSON.parse(str2)["property"][2]["value"]));
});
j++;
if (j == test.length) {
var sumTotal = 0;
var sumThree = 0;
var status = '';
for (var i in points) {
sumTotal += points[i];
}
var averageTotal = parseInt(Math.round(sumTotal / points.length));
for (var i = 0; i < 3; i++) {
sumThree += points[i];
}
var averageThree = parseInt(Math.round(sumThree / 3));
/*if(averageThree>averageTotal)
{
status='warning';
}
else
{
status='ok';
}*/
console.log('average: ' + averageThree + ' average 100 ' + averageTotal + ' status ' + status);
//send_event('speed', {current: averageThree/*, status: status*/, last: averageTotal});
j = 0;
points = [];
}
}
}, 15 * 1000);
so my question is how can i be sure my response 'point's' have always the same order. I've tried sending the var i to the callback function but can't get it to work
edit:
changed formatting.
The output of the first callback:
{
"count":100,
"nextHref":"/httpAuth/app/rest/builds/?locator=buildType:bt2,count:100,status:SUCCESS,start:100",
"build":[
{
"id":17469,
"number":"5075",
"status":"SUCCESS",
"buildTypeId":"bt2",
"startDate":"20140224T183152+0100",
"href":"/httpAuth/app/rest/builds/id:17469",
"webUrl":"http://x.x.x.x:8111/viewLog.html?buildId=17469&buildTypeId=bt2"
},
{
"id":17464,
"number":"5074",
"status":"SUCCESS",
"buildTypeId":"bt2",
"startDate":"20140224T165758+0100",
"href":"/httpAuth/app/rest/builds/id:17464",
"webUrl":"http://x.x.x.x:8111/viewLog.html?buildId=17464&buildTypeId=bt2"
},
{
"id":17461,
"number":"5073",
"status":"SUCCESS",
"buildTypeId":"bt2",
"startDate":"20140224T161852+0100",
"href":"/httpAuth/app/rest/builds/id:17461",
"webUrl":"http://x.x.x.x:8111/viewLog.html?buildId=17461&buildTypeId=bt2"
},
This output contains 100 items. From this output I take the id number and make a new request with this array of id's. This new callback gives me the build duration but the problem is because this happens asynchronously the response I get is not from the latest build but from the first response. So my question is how can i get these build speed array in the right order
It's not recommended to use anonymous function in a for loop.
The best way (for me), it's to use the async library.
A simple exemple to answer your question :
var objectList = [
{"name":"Doe", "firstname":"John", "position":1},
{"name":"Foo", "firstname":"Bar", "position":2},
{"name":"Gates", "firstname":"Bill", "position":3},
{"name":"Jobs", "firstname":"Steve", "position":4},
];
var arr = [];
async.each(objectList, function(person, callback) {
arr.push(person); // async.each is asynchronous, so at the end, the order will be bad
}, function(err) {
async.sortBy(arr, function(p, callback) { // Reorder
callback(err, p.position);
}, function(err, results) {
callback(null, results); // Send the result
});
});
This example will work for your issue.

Categories