How to post inside a loop with AngularJS 1.4.8 - javascript

I have found other answers to similar questions of mine, but none of them worked for me.
I'm trying to create a cards game. Here I'm trying to shuffle through the cards and give each player 5 random Money cards ( $scope.MoneyCards ). Instead, this code gives all the 15 cards to the last player on the loop.
The results with ( Angular 1.0.8 ) is exactly how I want it. But when I'm using any later version this doesn't work. I'm working on migrating to 1.4.8 .
This is the code I'm using:
$scope.StartGame = function() {
$http.get('/api/MoneyCards')
.success(function(data) {
$scope.MoneyCards = data;
console.log(data);
// Loop through players
player = 1;
while(player < 4) {
// Loop through money cards to give each player 5 cards
i=0;
while(i < 5) {
// Shuffle
var Mfloor = Math.floor(Math.random()*$scope.MoneyCards.length);
$scope.randomCard = $scope.MoneyCards.splice(Mfloor, 1 );
// Create the card in the Deck table and attach it to a player
$scope.createDecks($scope.randomCard[0],player,0);
i++;
}
player++;
}
})
.error(function(data) {
console.log('Error: ' + data);
});
};
This is the createDecks function
$scope.createDecks = function(card, player, type) {
$scope.formData.player = player;
$scope.formData.type = type;
$scope.formData.card = card;
$http.post('/api/Decks', $scope.formData)
.success(function(data) {
$scope.formData = {};
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});
};
I'll try to adopt this to JSFiddle.
Code provided by georgeawg using $q and still not working
$scope.StartGame = function() {
$http.get('/api/MoneyCards')
.success(function(data) {
$scope.MoneyCards = data;
console.log(data);
var player = 0;
while(player < 3){
var i = 0;
var qPromise = $q.when(0);
while(i < 5) {
// Shuffle
var Mfloor = Math.floor(Math.random()*$scope.MoneyCards.length);
var randomCard = $scope.MoneyCards.splice(Mfloor, 1 );
// Create the card in the Deck table and attach it to a player
qPromise = qPromise.then( function (response) {
return $q.all([
response,
$scope.createDecksPromise(randomCard[0],player,0)
]);
});
i++;
};
player += 1;
};
qPromise.then ( function(response) {
//do whatever
console.log(response);
}) .catch ( function (error) {
throw error;
});
})
.error(function(data) {
console.log('Error: ' + data);
});
};

First yourcreateDecks function should return promises.
$scope.createDecksPromise = function(card, player, type) {
var formData = {};
formData.player = player;
formData.type = type;
formData.card = card;
return $http.post('/api/Decks', formData);
};
Your iterative loop should then chain those promises.
var i = 0;
var qPromise = $q.when([]);
while(i < 5) {
// Shuffle
var Mfloor = Math.floor(Math.random()*$scope.MoneyCards.length);
var randomCard = $scope.MoneyCards.splice(Mfloor, 1 );
// Create the card in the Deck table and attach it to a player
// Add closure for randomCard and player
(function (randomCard, player) {
qPromise = qPromise.then( function (response) {
response.push(
$scope.createDecksPromise(randomCard[0],player,0)
);
return $q.all(response);
});
})(randomCard, player);
i++;
};
qPromise.then ( function(response) {
//do whatever
}) .catch ( function (error) {
throw error;
});
Remember that when chaining promises you should always return something to the .then method.
Also be aware that the .then method has a different response structure than the .success method.
The AngularJS team has come to their senses and deprecated .success. We should all be using the .then method now. To learn more about the deprecation (or should I say failure) of .success and .error, see the latest AngularJS $http API Docs.
To learn more about $q.when, look at the AngularJS $q Service API Docs.

I have finally found the answer, here is the code:
$scope.StartGame = function() {
$http.get('/api/MoneyCards')
.success(function(data) {
$scope.MoneyCards = data;
console.log(data);
var qPromise = [];
var player = 1;
while(player < 4){
var i = 0;
while(i < 5) {
qPromise.push([
Mfloor = Math.floor(Math.random()*$scope.MoneyCards.length),
randomCard = $scope.MoneyCards.splice(Mfloor, 1 ),
$scope.createDecksPromise(randomCard[0],player,0)
]);
i++;
};
player += 1;
};
return $q.all(qPromise);
})
.error(function(data) {
console.log('Error: ' + data);
});
};
Thanks to georgeawg, the $http.post $scope.createDeckPromise() should return promises:
$scope.createDecksPromise = function(card, player, type) {
var formData = {};
formData.player = player;
formData.type = type;
formData.card = card;
return $http.post('/api/Decks', formData);
};
For whoever is having the same issue. This is the solution without my code:
$scope.RunCode(){
var Promises = [];
var i = 0;
while (i < 5){
Promises.push($http.post('/api/whatever', formData));
// to add multiple functions to the promise use [], example: Promises.push([doThing1, doThing2]);
i++;
};
return $q.all(Promises);
}

Related

Unable to return result from promise

I am trying to read some data from 2 different tables and parse a CSV file before rendering an ejs file.
I can get the data from both tables and from the CSV file but I seem to be unable to return the result.
Pretty sure this is a problem with the way I handle async execution but I fail to see what I am doing wrong.
I've spent the last 2 days reading about this (including the threads around here) and browsing but somehow the answer still escapes me.
First file - usercms.js
app.get('/userscms', function(req, res)
{
existingUsers.getExistingUsers()
.then(function(appUsers)
{
//global users array
//I can display these in my ejs file
globalAppUsers = appUsers;
})
.then(existingUsersAttributesQlik.getExistingUsersAttributesQlik())
.then(function(usersQlikAttributes)
{
//global user attributes array
//undefined data
globalUsersQlikAttributes = usersQlikAttributes;
})
.then(existingSuppliers.parseSuppliersCSV())
.then(function(supplierData)
{
//the result I am expecting
//this prints undefined
console.log(supplierData);
}).then(function()
{
res.render('userscms.ejs',
{
users: globalAppUsers,
attributes: globalUsersQlikAttributes
});
});
});
Second function - getxistingUsers.js (identical to the getExistingUsersAttributesQlik, except for the query)
var userData = [];
var appUsers = [];
(function (exports)
{
exports.getExistingUsers = function ()
{
return promisemysql.createConnection(dbconfig.development).then(function(conn)
{
var result = conn.query("SELECT id, username, firstName, lastName, email, phone, lastLogin, isAdmin, isValid, isPhoneValid, accountCreationDateTime FROM Users");
conn.end();
return result;
}).then(function(rows)
{
return rows;
}).then(function(rows)
{
if (rows.length)
{
userData = [];
appUsers = [];
rows.forEach(function (elem)
{
userData.push(_.toArray(elem));
});
for (i = 0; i < userData.length; i++)
{
var appUser = new appUserModel.AppUser(
userData[i][0],
userData[i][1],
userData[i][2],
userData[i][3],
userData[i][4],
userData[i][5],
userData[i][6],
userData[i][7],
userData[i][8],
userData[i][9],
userData[i][10]);
appUsers.push(_.toArray(appUser));
}
return appUsers;
}
else
{
console.log("NOPE");
return null;
}
}).then(function(appUsers)
{
console.log(appUsers);
return appUsers;
});
};
})(typeof exports === 'undefined' ? this['getExistingUsers'] = {} : exports);
Third file - parseSuppliersCSV.js
var supplierData = [];
var suppliersData = [];
var csvCount = 0;
(function (exports)
{
exports.parseSuppliersCSV = function ()
{
return new Promise(function(resolve, reject)
{
var fileStream = fs.createReadStream("myCSV.csv");
var parser = fastCsv();
csvCount = 0;
supplierData = [];
suppliersData = [];
fileStream
.on("readable", function ()
{
var data;
while ((data = fileStream.read()) !== null)
{
parser.write(data);
}
})
.on("end", function ()
{
parser.end();
});
parser
.on("readable", function ()
{
var data;
while ((data = parser.read()) !== null)
{
if(csvCount >= 1)
{
csvCount++;
var arrayOfStrings = data[0].split(';');
var supplier = new supplierModel.Supplier(arrayOfStrings[0],arrayOfStrings[1]);
suppliersData.push(_.toArray(supplier));
}
else
{
csvCount++;
}
}
})
.on("end", function ()
{
console.log("done");
//all OK here
console.log(suppliersData);
//this doesn't seem to return anything
return suppliersData;
});
});
};
})(typeof exports === 'undefined' ? this['parseSuppliersCSV'] = {} : exports);
Any ideas what I am doing wrong? Am I approaching this the wrong way?
I'll take a guess here and assume the promise you created should resolve to something...instead of returning a value.
.on("end", function ()
{
console.log("done");
//all OK here
console.log(suppliersData);
//this doesn't seem to return anything
return resolve(suppliersData);
});

Node.js Promises within promises not waiting for for loop to return data

The return Promise.all([photoArray]) returns an empty array, seemingly not waiting for the callFB to return its promise that then pushes into the array.
I am not sure what I am doing wrong but am relatively new to Promises with for loops and Ifs.
I am not sure exactly if I am using the correct number of Promises but I seem to not be able to get the 3rd tier Promise.all to wait for the for loop to actually finish (in this scenario, the for loop has to look through many item so this is causing an issue where it is not triggering callFeedback for all the items it should before context.done() gets called.
I have tried using Q.all also for the Promise.all([photoArray]) but have been unable to get that working.
module.exports = function (context, myBlob) {
var res = myBlob
var promiseResolved = checkPhoto(res,context);
var promiseResolved2 = checkVideo(res,context);
Promise.all([promiseResolved, promiseResolved2]).then(function(results){
context.log(results[0], results[1]);
// context.done();
});
});
};
};
function checkPhoto(res, context){
return new Promise((resolve, reject) => {
if (res.photos.length > 0) {
var photoArray = [];
for (var j = 0; j < res.photos.length; j++) {
if (res.photos[j].feedbackId !== null){
var feedbackId = res.photos[j].feedbackId;
var callFB = callFeedback(context, feedbackId);
Promise.all([callFB]).then(function(results){
photoArray.push(results[0]);
});
} else {
photoArray.push("Photo " + j + " has no feedback");
}
}
return Promise.all([photoArray]).then(function(results){
context.log("end results: " + results);
resolve(photoArray);
});
} else {
resolve('No photos');
}
})
}
function checkVideo(res, context){
return new Promise((resolve, reject) => {
same as checkPhoto
})
}
function callFeedback(context, feedbackId) {
return new Promise((resolve, reject) => {
var requestUrl = url.parse( URL );
var requestBody = {
"id": feedbackId
};
// send message to httptrigger to message bot
var body = JSON.stringify( requestBody );
const requestOptions = {
standard
};
var request = https.request(requestOptions, function(res) {
var data ="";
res.on('data', function (chunk) {
data += chunk
// context.log('Data: ' + data)
});
res.on('end', function () {
resolve("callFeedback: " + true);
})
}).on('error', function(error) {
});
request.write(body);
request.end();
})
}
The code suffers from promise construction antipattern. If there's already a promise (Promise.all(...)), there is never a need to create a new one.
Wrong behaviour is caused by that Promise.all(...).then(...) promise isn't chained. Errors aren't handled and photoArray.push(results[0]) causes race conditions because it is evaluated later than Promise.all([photoArray])....
In case things should be processed in parallel:
function checkPhoto(res, context){
if (res.photos.length > 0) {
var photoArray = [];
for (var j = 0; j < res.photos.length; j++) {
if (res.photos[j].feedbackId !== null){
var feedbackId = res.photos[j].feedbackId;
var callFB = callFeedback(context, feedbackId);
// likely no need to wait for callFB result
// and no need for Promise.all
photoArray.push(callFB);
} else {
photoArray.push("Photo " + j + " has no feedback");
}
}
return Promise.all(photoArray); // not [photoArray]
} else {
return 'No photos';
};
}
callFB promises don't depend on each other and thus can safely be resolved concurrently. This allows to process requests faster.
Promise.all serves a good purpose only if it's used to resolve promises in parallel, while the original code tried to resolve the results (results[0]).
In case things should be processed in series the function benefits from async..await:
async function checkPhoto(res, context){
if (res.photos.length > 0) {
var photoArray = [];
for (var j = 0; j < res.photos.length; j++) {
if (res.photos[j].feedbackId !== null){
var feedbackId = res.photos[j].feedbackId;
const callFBResult = await callFeedback(context, feedbackId);
// no need for Promise.all
photoArray.push(callFBResult);
} else {
photoArray.push("Photo " + j + " has no feedback");
}
}
return photoArray; // no need for Promise.all, the array contains results
} else {
return 'No photos';
};
}
Add try..catch to taste.

Braintree Transaction.search in Meteor Server

How can I wait for the Braintree Transaction.search() function to return all data.
Right now it does not wait and just comes back with undefined return value.
Here is the code
I tried to use Meteor.asynwrap but that also does not work.
`
function getTrxns(cid) {
var future = new Future();
var trxns = [];
var i = 0
var stream = gateway.transaction.search(function (search) {
r = search.customerId().is(cid)});
stream.on("data", function(data){
i = i+1
trxns.push({
'id':data.id,
'amount':data.amount,
'crtDt': data.createdAt,
'ccType': data.creditCard.cardType,
'currency': data.currencyIsoCode,
'last4': data.creditCard.last4,
'expdt': data.creditCard.expirationDate
});
});
stream.on("end", function(){
// print the output in console
console.log('End Stream cnt: '+i);
return trxns;
});
stream.resume();
}
Meteor.methods({
findCustTrxns: function() {
var btId = Meteor.user().custBtId;
if (!btId) { return []; };
console.log('findCustTrxns cusBtId: '+btId);
var xx = getTrxns(btId);
console.log('xx len :'+xx.length);
}
});
OUTPUT is:
I20170509-15:22:09.095(0)? findCustTrxns cusBtId: 232057823
I20170509-15:22:09.095(0)? Exception while invoking method 'findCustTrxns' TypeError: Cannot read property 'length' of undefined
I20170509-15:22:09.095(0)? End Stream cnt: 56
Found a way to make it work:
1. Added a callback function
function getTrxns(cid,callback )
2. invoked the callback in stream.on('end;..) Here is the code
function getTrxns(cid,callback ) {
var trxns = [];
var i = 0
var stream = gateway.transaction.search(function (search) {
r = search.customerId().is(cid)});
stream.on("data", function(data){
i = i+1
trxns.push({
'id':data.id,
});
});
stream.on("end", function(){
// print the output in console
console.log('End Stream cnt: '+i);
callback('', trxns);
});
stream.resume();
}
3. Changed the Meteor Method :
findCustTrxns: function(btId) {
if (!btId) { return []; };
console.log('findCustTrxns cusBtId: '+btId);
var trxns = [];
var i = 0;
var fn = Meteor.wrapAsync(getTrxns); //made it a synchronous call
try {
var res = fn(btId);
if (res) {
console.log('Got data from getTrxns ');
return res;
}
} catch( err) {
console.log('Error calling hetTrxns '+err);
}
}, //findCustTrxns
Now I am able to get the Transactions. Hope it helps

Promises issue with async and sync - angular

Working on an mobile cordova/angular project. Below is a simple service call:
this.getSomeData = function (businessId) {
var deferred = $q.defer();
var query = "SELECT * FROM Stuff";
$cordovaSQLite.execute(db, query).then(function (res) {
deferred.resolve(res.rows);
}, function (err) {
deferred.reject(err);
});
return deferred.promise;
};
The issue is simple:
for (var k = 0; k < count; k++) {
myService.getSomeData($scope.model.stuff[k].id, k).then(function (data) {
// whatever
}
);
getSomeData is async, so by the time it returns, the k of the for cycle is far from correct.
I thought of passing k to the service method as a parameter:
for (var k = 0; k < count; k++) {
myService.getSomeData($scope.model.stuff[k].id, k).then(function (data) {
// whatever
}
);
And change the service method accordingly:
this.getSomeData = function (id, index) {
var deferred = $q.defer();
var query = "SELECT * FROM Stuff";
$cordovaSQLite.execute(db, query).then(function (res) {
deferred.resolve(res.rows, index);
}, function (err) {
deferred.reject(err);
});
return deferred.promise;
};
But that second parameter is ignored and is always undefined.
How to overcome this?
It sounds like you're running into a problem called "closure over the loop variable" and this issue is discussed in detail here:
JavaScript closure inside loops – simple practical example
In your case, however, the clean solution is to combine Array#map with $q.all():
$q.all($scope.model.visits.map(function (stuff) {
return myService.getSomeData(stuff.id);
})).then(function (results) {
// results is an array of the results of all the calls to getSomeData() in the correct order
});
Also, as Bergi points out, avoid the deferred antipattern:
this.getSomeData = function (id) {
var query = "SELECT * FROM Stuff";
return $cordovaSQLite.execute(db, query).then(function (res) {
return res.rows;
});
};
This is how I got it working. I tried using #JLRishe's suggestion but it wouldn't work. Turns out, I managed to pass more than one parameter through to the service method and back to the controller as well (by building an object than contains as many parameters I need).
myService.getSomeData().then(
function (stuff) {
// whatever
}
).then(function () {
for (var i = 0; i < $scope.model.stuff.length; i++) {
// HERE I SEND TWO PARAMETERS TO THE SERVICE METHOD
myService.getSomeMoreData($scope.model.stuff[i].id, i).then(
function (data) {
// whatever
}
);
}
});
this.getSomeMoreData = function (id, index) {
var deferred = $q.defer();
var query = "SELECT * FROM stuff";
$cordovaSQLite.execute(db, query).then(function (res) {
var moreStuff = [];
for (var i = 0; i < res.rows.length; i++) {
var junk = res.rows.item(i);
moreStuff.push(junk);
}
// HERE I RESOLVE AN OBJECT INSTEAD OF TWO PARAMETERS
deferred.resolve({
moreStuff: moreStuff,
index: index
});
}, function (err) {
deferred.reject(err);
});
return deferred.promise;
};

How to handle each response in successCallBack of promises in case of dynamic passage of promises to .when()

Here are the cloud functions namely 'batchReq1' and batchPromises.
In any case, if I know the exact number of promises pushed (Consider the size of results to be '2' in function batchPromises(results)) and executed through when(), I can handle the success response by passing that number of result parameters (In the below example request1, request2) in successCallBack of .then().
If I have to process the number of promises pushed to .when() dynamically, then, how can we handle this in SuccessCallBack? Unlike earlier scenario, we can't expect fixed number of results in the then method (batchPromises(results).then(function (result1, result2) {....)
batchReq1
Parse.Cloud.define("batchReq1", function (request, response) {
var results = request.params.imageArray;
batchPromises(results).then(function (result1, result2) {
console.log("Final Result:: Inside Success");
console.log("Final Result:: Inside Success result 1::::"+result1);
console.log("Final Result:: Inside Success result 2::::"+result2);
response.success();
}
// batchPromises(results).then(function (arraySuccess) {
//
// console.log("Final Result:: Inside Success");
// console.log("Final Result:: Inside Success:: Length:: "+arraySuccess.length);
// //Fetch all responses from promises and display
// var _ = require('underscore.js');
// _.each(arraySuccess, function (result) {
//
// console.log("Final Result:: " + result)
//
// });
//
//
// response.success();
//
// }
, function (error) {
console.log("Final Result:: Inside Error");
response.error(error);
});
});
batchPromises
function batchPromises(results) {
var promise = Parse.Promise.as();
var promises = [];
var increment = 0;
var isFromParallelExecution = false;
var _ = require('underscore.js');
_.each(results, function (result) {
var tempPromise = Parse.Promise.as("Promise Resolved ");
promises.push(tempPromise);
}
promise = promise.then(function () {
return Parse.Promise.when(promises);
});
}
increment++;
});
return promise;
}
this is how i handle this...
Parse.Cloud.define("xxx", function(request, response)
{
var channels = ["channel1", "channel2", "channel3", "channel4", "channel5"];
var queries = new Array();
for (var i = 0; i < channels.length; i++)
{
var query = new Parse.Query(channels[i]);
queries.push(query.find());
}
Parse.Promise.when(queries).then(function()
{
var msg = "";
for (var j = 0; j < arguments.length; j++)
{
for (var k = 0; k < arguments[j].length; k++)
{
var object = arguments[j][k];
msg = msg + " " + object.get('somefield');
}
}
response.success(msg);
});
});
Either you just use the arguments object to loop over the results, or you build your arraySuccess without when - it doesn't make much sense here anyway as you batch the requests (executing them sequentially), instead of executing them in parallel:
function batchPromises(tasks) {
var _ = require('underscore.js');
_.reduce(tasks, function (promise, task) {
return promise.then(function(resultArr) {
var tempPromise = Parse.Promise.as("Promise Resolved for "+taks);
return tempPromise.then(function(taskResult) {
resultArr.push(taskResult);
return resultArr;
});
});
}, Parse.Promise.as([]));
}
If you actually wanted to execute them in parallel, use a simple
function batchPromises(tasks) {
var _ = require('underscore.js');
return Parse.Promise.when(_.map(tasks, function (task) {
return Parse.Promise.as("Promise Resolved for "+taks);
}).then(function() {
return [].slice.call(arguments);
});
}

Categories