I try to iterate throug an array of ids and make a ajax request for each id. subsequentely each response object is pushed in an array/ so far no problem, however, the problem starts when i try to access the responses in the array. the strange thing is that in the console log the responses are shown (ouside the array though, see below) but the properties of the array objects are empty/ it seems i generated an empty object with some data attached to it/ my question is how can i access the objects that are in (or not in?) the array
var getAssoc = {
returnProds: function (idCache) {
var id = idCache;
var prodData = [];
var counter = id.length;
$.each(id, function (i) {
$.ajax({
url: "myurl.php?",
data: {
'id': id[i]
},
success: function (data) {
prodData[i] = data;
counter--;
if (counter === 0) console.log(prodData);
},
})
});
}
};
console log looks like this. testing for number of properties returns 0
[]
0 Object { array={...}}
1 Object { array={...}}
2 Object { array={...}}
3 Object { array={...}}
In you success callback Try parsing the response coming from your server:
jQuery.parseJSON(data);
Related
I'm fetching a URL. The full response is spread over five pages.
I'm looping through each pages which returns me an array of object (please correct me if I'm wrong):
[{item_1=foo, item_2=bar, item_3=foobar, value_1=XX}, {item_1=bar, item_2=foo, item_3=barfoo, value_1=XX},etc...]
I want to consolidate all the response like if it was one big array of objects.
So far, I wrote this:
for (i = 1; i <= total_pages; i++) {
var rawResponse = UrlFetchApp.fetch(
'url',
{
method: 'GET'
})
response[i] = JSON.parse(rawResponse);
}
var g = response[1].concat(response[2], response[3],response[4],response[5]);
g contains the desired output; however, as you can see, this is not dynamic. How can I solve this? I could you the push method, but I would return me a new array with each response.
In order to make your code "dynamic" you could use the concat function inside the for-loop, for each of the pages. A possible modification of your code could look like the following, where the result variable would contain all the results:
var result = [];
for (var i = 1; i <= total_pages; i++) {
var rawResponse = UrlFetchApp.fetch(
'url',
{
method: 'GET'
}
);
var current = JSON.parse(rawResponse);
result = result.concat(current);
}
In this code I:
Get a list of items (via AJAX)
Get more items details (via 2nd AJAX call)
Push item + details to history array
Code:
<template is="dom-repeat" items="[[history]]" index-as="index">
<p>[[item.itemId]]>
<p>[[item.priceNet]]</p>
</template>
...
getRequest() {
let request = this.$.ajax.generateRequest();
request.completes.then(req => {
// returns this.data;
this.i = 0;
console.log(this.history); // Logs several objects in array on 1st iteration
this.getRequest2();
})
}
getRequest2() {
if (this.i<this.data.length) {
this.itemId = this.data[this.i].itemId;
let request = this.$.ajax2.generateRequest();
request.completes.then(req => {
this.updateHistory();
})
}
}
updateHistory() {
if(typeof this.responseStock.error == 'undefined') {
this.push('history', {
"itemId": this.response[0][this.i].itemId,
"priceNet": this.responseStock.priceNet
})
} else {
this.push('history', {
"itemId": this.response[0][this.i].itemId,
"priceNet": null
})
}
this.i = this.i + 1;
this.getRequest2();
}
However, upon the first instance history logs multiple objects in the array, so the push method doesn't properly update.
How can I pass the data through so that each item that gets returned from the 1st AJAX call gets attributed with the 2nd AJAX call if attributes exist, and then push it to an array?
Note: the this.push is from the Polymer framework, but the problem seems to be that the timing of the push method is off...
I try to set up this example https://github.com/AngularClass/angular-websocket#usage
Here is my code
App.factory('MyData', function($websocket, $q) {
var dataStream = $websocket('wss://url');
var collection = [];
dataStream.onMessage(function(message) {
var result = JSON.parse(message.data);
console.log(result);
collection = result;
});
var methods = {
collection: collection,
get: function() {
dataStream.send(JSON.stringify({
api: "volume",
date: "2017-02-01",
interval: 600
}));
}
};
return methods; });
In my controller I wrote:
$interval(function () {
console.log(MyData.collection);
}, 1000);
The problem is that I don't receive any values, however on message arrive I see console log, so websocket itself is obviously alive. If I change collection.push(result) (like in example) I receive constantly growing array. I need only the last value, however. Why collection = result is wrong ?
var collection = []; instantiates a new array and its reference is stored in the variable collection. Then, this reference is assigned to methods.collection and, hence, MyData.collection. However, with JSON.parse a new array is instantiated. collection = result; overwrites the original reference with the reference of the new array. But MyData.collection still holds the reference to original array.
So, there are two ways to encounter the problem:
Don't overwrite the reference to the original array. push is good, but before, you need to clear the array in order to only show the last value.
collection.splice(0, collection.length);
collection.push(result);
However, that would be an array in an array. You probably need to push the values individually (Array.concat will create a new array, too):
collection.splice(0, collection.length);
result.forEach(function(value) {
collection.push(value);
});
Assign the reference of the new array directly to methods.collection. In this case, no extra variable collection is needed.
App.factory('MyData', function($websocket, $q) {
var dataStream = $websocket('wss://url');
var methods = {
collection: [],
get: function() {
dataStream.send(JSON.stringify({
api: "volume",
date: "2017-02-01",
interval: 600
}));
}
};
dataStream.onMessage(function(message) {
var result = JSON.parse(message.data);
console.log(result);
methods.collection = result;
});
return methods;
});
I am using sails (0.11.0) running on nodejs (6.9.1). I am trying to construct an array by filling it through for loop. I would send this completed array in response to the client. I have tried various methods as suggested by people here on Stack Overflow, for example
the discussion here suggested
for (var i = yearStart; i < yearEnd+1; i++) {
arr.push(i);
}
On this discussion, it is suggested to use:
var array = calendars.map(function(item) {
return item.id;
});
console.log(array);
Similarly I tried many methods but I am coming across the same issue that during the loop, the array gets filled but as soon as the loop is completed, the array gets empty because of asynchronous process and therefore I can not send the response. To tackle with this I tried checking the index inside the loop body and send response from inside the loop body itself through
var userArray = [];
_.each(users, function(user, index){
MySQLConnector.query('CALL user_image (?)', [user.id], function(err, userImage){
if(err){
return res.json({"status":"some_error"});
}else{
userID = user.id
userImageID = userImage[0][0].id;
var userInfo = {
userID: userID,
userImageID: userImageID
}
userArray.push(userInfo)
if(index == users.length - 1){
res.json({selectedUsers: userArray});
}
}
});
});
I am initiating an empty userArray and then iterate through users object where each element of the object is characterized by name user and an index. Through a MySQL query I am fetching the userImage object and in each iteration, I am creating an object called userInfo that consists of userID and userImageID. I am pushing this object into userArray. And after each iteratio of the for loop (_.each), I check if last index is reached. Once last index is reached, the final array is sent as response before loop body is complete.
Here too I have an issue that the array body is not always completely filled. The reason is due to asynchronous process, the index does not always follow the order 0,1,2,3,4,.... and it can start with any number and can jump to any index in the next iteration, for example the first index to start would be 4, the second would be 0, third would be 2 and so on. This sequence would be different for every time we run this for loop. For a user, it will appear to be a total random process. Therefore if users.length is 8, and current index is randomly 7 at third iteration, the condition index == users.length - 1 will be met and response will be sent just with an array consisting of 3 elements rather than 8.
Can someone suggest me a better and robust way to fill an array through the for loop in nodejs and send that array in response, so that all items are included in the array in their original order?
As you are using node js , it is better to use any promises library like bluebird or async to handle Async requests.
The reason your loop is not working as expected is because as you've pointed out, due to async requests taking time to resolve for which _.each loop is not waiting.
Using bluebird, it can be done with Promise.map method which works as explained below from the documentaion :
Given an Iterable(arrays are Iterable), or a promise of an Iterable,
which produces promises (or a mix of promises and values), iterate
over all the values in the Iterable into an array and map the array to
another using the given mapper function.
Promises returned by the mapper function are awaited for and the
returned promise doesn't fulfill until all mapped promises have
fulfilled as well. If any promise in the array is rejected, or any
promise returned by the mapper function is rejected, the returned
promise is rejected as well.
Hence, Using Promise.map your code can be updated like below :
var Promise = require("bluebird");
return Promise.map(users, function(user, index){
return MySQLConnector.query('CALL user_image (?)', [user.id], function(err, userImage){
if(err){
return Promise.reject({"status":"some_error"});
}else{
userID = user.id
userImageID = userImage[0][0].id;
var userInfo = {
userID: userID,
userImageID: userImageID
}
return userInfo;
}
});
})
.then(function (usersArray){
res.json({selectedUsers: usersArray});
})
.catch(function (err){
res.json(err);
});
You can execute loops with functions with callbacks synchronously using SynJS:
var SynJS = require('synjs');
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'tracker',
password : 'tracker123',
database : 'tracker'
});
function myFunction1(modules,connection,users) {
var ret=[];
for(var i=0; i<users.length; i++) {
connection.query("SELECT CONCAT('some image of user #',?) AS userImage", [users[i]], function(err, rows, fields) {
if (err) throw err;
ret.push({
id: users[i],
image: rows[0].userImage
});
modules.SynJS.resume(_synjsContext); // <-- indicate that callback is finished
});
SynJS.wait(); // <-- wait for callback to finish
}
return ret;
};
var modules = {
SynJS: SynJS,
mysql: mysql,
};
var users = [1,5,7,9,20,21];
SynJS.run(myFunction1,null,modules,connection,users,function (ret) {
console.log('done. result is:');
console.log(ret);
});
Result would be following:
done. result is:
[ { id: 1, image: 'some image of user #1' },
{ id: 5, image: 'some image of user #5' },
{ id: 7, image: 'some image of user #7' },
{ id: 9, image: 'some image of user #9' },
{ id: 20, image: 'some image of user #20' },
{ id: 21, image: 'some image of user #21' } ]
I calling getBubblesUserAccess that returns json objects that are orderd in a special way. This results i wanna run a foreach and get other messages but there i wanna return them in "order". I know that it will run these async but there must be a way that i can force it to "sequential" execution. (above code is my last attempt to add a defer...)
Example
pseudo code - get my groups
{
"id":"016cd1fc-89a3-4e4a-9e6e-a102df1b03d9",
"parent":"53750396-7d26-41f3-913d-1b93276b9e09",
"name":"XX",
"createdBy":"c9c63080-2c5b-4e8e-a093-2cfcd628a9d0",
"hasWriteAccess":true,
"hasCreateAccess":false,
"hasDeleteAccess":false,
"hasAdminAccess":false,
"settingsBubbleId":"00000000-0000-0000-0000-000000000000"
},
{
"id":"016cd1fc-89a3-4e4a-9e6e-a102df1b03d9",
"parent":"53750396-7d26-41f3-913d-1b93276b9e09",
"name":"XX",
"createdBy":"c9c63080-2c5b-4e8e-a093-2cfcd628a9d0",
"hasWriteAccess":true,
"hasCreateAccess":false,
"hasDeleteAccess":false,
"hasAdminAccess":false,
"settingsBubbleId":"00000000-0000-0000-0000-000000000000"
}
From this result i wanna iterate over those parent id strings and call another service that respond with this.
pseudo code
for each group above call another service with parent id and get result. This result will be added to a new JSON object.
"messages":[
{
"id":"f1d1aeda-d4e2-4563-85d5-d954c335b31c",
"text":"asd",
"sent":"2015-09-10T22:31:09.897+00:00",
"sender":"6b9e404b-ef37-4d07-9267-3e7b2579003b",
"senderName":"XXX XXXX"
},
{
"id":"a7ac0432-e945-440e-91ce-185170cbf3de",
"text":"asd",
"sent":"2015-09-10T22:28:24.383+00:00",
"sender":"c9c63080-2c5b-4e8e-a093-2cfcd628a9d0",
"senderName":"ZZZZZ ZZZZ"
},
My problem is that my second foreach are running async (as it should) and i want it to resolve back in same order as first json object...
My code::
var loadBubblesAccess = function () {
if (vm.running && angular.isDefined(vm.running)) { return; }
vm.running = true;
vm.bubblesWithMessages = null;
return BubbleFactory.getBubblesUserAccess().then(function (bubblesAccessTo) {
return bubblesAccessTo;
});
},
loadSubBubbles = function (bubblesAccessTo) {
/**
* Result from chain method with all bubbles user has access to.
*/
var promiseArray = [];
//var promiseArrayError = [];
var i = 0;
/**
* Creates a defer object so that we will not resolve before for each loop has been gone thru.. async problems.
*/
var deferred = $q.defer();
angular.forEach(bubblesAccessTo, function (bubble) {
$log.error(JSON.stringify(bubblesAccessTo));
/**
* Get 20 because default thats default and cache and e-tags are done to that number..
*/
BubbleFactory.getBubbleMessages(bubble.id, 0, 20, false).then(function (data) {
i++;
if (data.messages.length > 0) {
promiseArray.push({ bubbleSortOrder: i, bubbleId: bubble.parent, bubbleName: bubble.name, bubbleMessagesId: bubble.id, bubbleMessages: smartTrim(data.messages[0].text, 400, ' ', ' ...'), bubbleMessagesSent: data.messages[0].sent });
}
else {
// console.log("YYYY::: " + bubble.parent);
promiseArray.push({ bubbleSortOrder:i, bubbleId: bubble.parent, bubbleName: bubble.name, bubbleMessagesId: bubble.id, bubbleMessages: 'Inget meddelande än..', bubbleMessagesSent: '' });
}
});
/**
* Check if we have gone thru all bubbles - when finished we resolve defer object.
*/
if(i===bubblesAccessTo.length)
{
deferred.resolve(promiseArray);
}
});
//$log.debug.log(promiseArray);
vm.bubblesWithMessages = promiseArray;
promiseArray.length = 0;
vm.running = false;
};
loadBubblesAccess().then(loadSubBubbles);
The $q service in AngularJS is described as "lightweight" because it only implements the functions 90% of people need. That keeps its code size small - at the expense of not being able to address requests like yours very easily.
If you have the option, try an alternative such as bluebird. Bluebird provides a reduce() function that can execute an array of promises serially, and return their results in the order they were requested. It makes this task straightforward because your result array will match your data array and you can match up the results very easily.
If you do NOT have that option, there is a standard (if not-exactly-simple) technique with promises where you build an array of the elements you want to promise, then call the processing function (that returns a Promise) on the first value (popped from the array). In the .finally() handler, call the processing function recursively with the next value until it is empty (or an error occurs).
Pseudo-code for this:
var valuesToProcess = [1, 2, 3],
results = [];
function processValue(val) {
myProcessingFunction(val).then(function(result) {
results.push(result);
}).catch(function(e) {
console.log('FAIL!', e);
}).finally(function() {
if (valuesToProcess.length > 0) {
processValue(valuesToProcess.shift());
} else {
// All done - do something with results here
}
});
}
// Note: No error checking done, assumes we have work to do...
processValue(valuesToProcess.shift());
You'll need to adapt this to your use-case but it's a simple technique that guarantees serial operation and result-handling.