I'm writing an iOs app with Parse.com and Cloud Code. Actually I want to retrieve objects which contain one picture and other informations from a website and I want to add them to a class named News. When I run my code, every object is saved (in my class, one row = one retrieved object) but unfortunately the only first one has its picture saved.... Any idea ?
I made a lot of searches about promises (series / parallels) and I think the problem comes from here..
Note : don't worry about myLink, myImgLink : I put this to make my code easy to read !
Parse.Cloud.define("rajouteNews", function(request, response) {
Parse.Cloud.httpRequest({ url: 'myUrl'}).then(function(httpResponse) {
var news = [];
var NewsClass = Parse.Object.extend("news");
for (var i = 0; i < 10 ; ++i) {
var maNews = new NewsClass();
maNews.set("link", myLink[i]); // "Other informations"
maNews.set("imgLink", myImgLink[i]);
maNews.set("title", myTitle[i]);
var promises = [];
promises.push(Parse.Cloud.httpRequest({
url: $('img').attr('src'),
method: 'GET',
}).then(function(httpResponse){
var imgFile = new Parse.File("photo.jpg", {base64:httpResponse.buffer.toString('base64')});
maNews.set("image",imgFile); // The picture
return maNews.save();
}));
news.push(maNews);
}
promises.push(Parse.Object.saveAll(news, {
success: function (list) {
response.success(news.length.toString() + " ont été sauvegardées");
},
error: function (list, err) {
response.error("Error adding news");
}
}));
return Parse.Promise.when(promises);
}).then(function(bla,result){
response.success("Job done");
}, function(error) {
response.error(error);
}
);
});
Your promises array should put out of the for loop scope. Otherwise , your promise array would be assigned to be a new blank array each loop.
Parse.File would be saved automaticly when its parent do save, you don't need to save it in advance.
So I improve your code as following, try it and tell me weather it works.
Parse.Cloud.define("rajouteNews", function(request, response) {
Parse.Cloud.httpRequest({
url: 'myUrl'
}).then(function(httpResponse) {
var promises = [];
var NewsClass = Parse.Object.extend("news");
for (var i = 0; i < 10; ++i) {
var maNews = new NewsClass();
maNews.set("link", myLink[i]); // "Other informations"
maNews.set("imgLink", myImgLink[i]);
maNews.set("title", myTitle[i]);
var maNewsPromise = Parse.Cloud.httpRequest({
url: $('img').attr('src'),
method: 'GET',
}).then(function(httpResponse) {
var imgFile = new Parse.File("photo.jpg", {
base64: httpResponse.buffer.toString('base64')
});
maNews.set("image", imgFile); // The picture
return maNews.save();
});
promises.push(maNewsPromise);
}
return Parse.Promise.when(promises)
}).then(function(bla, result) {
// this function is call when `Parse.Promise.when(promises)` is done,
//I can't figure out why you take two params.
response.success("Job done");
}, function(error) {
response.error(error);
});
});
Related
I want to get some data about places using the Google Places API.
Thing is, I want to get data from more than 1000 records, per city of the region I'm looking for.
I'm searching for pizzeria, and I want all the pizzerias in the region I've defined. So I have an array like this:
['Pizzeria+Paris','Pizzeria+Marseille','Pizzeria+Nice','Pizzeria+Toulouse']
My objective is to make a single request, then wait 3sec(or more), and then process the second request. I'm using Lodash library to help me iterate.
Here is my code:
function formatDetails(artisan){
var latitude = artisan.geometry.location.lat;
var longitude = artisan.geometry.location.lng;
var icon = artisan.icon;
var id = artisan.id;
var name = artisan.name;
var place_id = artisan.place_id;
var reference = artisan.reference;
var types = artisan.types.toString();
$('#details').append('<tr>'+
'<td>'+latitude+'</td>'+
'<td>'+longitude+'</td>'+
'<td>'+icon+'</td>'+
'<td>'+id+'</td>'+
'<td>'+name+'</td>'+
'<td>'+place_id+'</td>'+
'<td>'+reference+'</td>'+
'<td>'+types+'</td>'+
'</tr>');
}
var getData = function(query, value){
$.ajax({
url: query,
type: "GET",
crossDomain: true,
dataType: "json",
success: function(response) {
var artisan = response.results;
console.log(artisan);
for (var i = 0; i < artisan.length; i++){
formatDetails(artisan[i]);
setTimeout(function(){console.log('waiting1');},3000);
}
setTimeout(function(){console.log('waiting2');},3000);
},error: function(xhr, status) {
console.log(status);
},
async: false
});
}
$(document).ready(function(){
var places =
['Pizzeria+Paris','Pizzeria+Marseille','Pizzeria+Nice','Pizzeria+Toulouse'];
_.forEach(places, function(value, key) {
var proxy = 'https://cors-anywhere.herokuapp.com/';
var target_url = 'https://maps.googleapis.com/maps/api/place/textsearch/json?query='+value+'&key=AIzaSyAClTjhWq7aFGKHmUwxlNUVBzFpIKTkOrA';
var query = proxy + target_url;
getData(query, value);
});
});
I've tried a lot of solutions I found on stackoverflow, but no one were working, or I might have done them wrong.
Thanks for your help!
The fact that $.ajax returns a Promise makes this quite simple
Firstly, you want getData to return $.ajax - and also get rid of async:false
var getData = function(query, value) {
return $.ajax({
url: query,
type: "GET",
crossDomain: true,
dataType: "json",
success: function(response) {
var artisan = response.results;
for (var i = 0; i < artisan.length; i++){
formatDetails(artisan[i]);
}
},error: function(xhr, status) {
console.log(status);
}
});
}
Then, you can use Array.reduce iterate through the array, and to chain the requests together, with a 3 second "delay" after each request
Like so:
$(document).ready(function(){
var places = ['Pizzeria+Paris','Pizzeria+Marseille','Pizzeria+Nice','Pizzeria+Toulouse'];
places.reduce((promise, value) => {
var proxy = 'https://cors-anywhere.herokuapp.com/';
var target_url = 'https://maps.googleapis.com/maps/api/place/textsearch/json?query='+value+'&key=AIzaSyAClTjhWq7aFGKHmUwxlNUVBzFpIKTkOrA';
var query = proxy + target_url;
return promise.then(() => getData(query, value))
// return a promise that resolves after three seconds
.then(response => new Promise(resolve => setTimeout(resolve, 3000)));
}, Promise.resolve()) /* start reduce with a resolved promise to start the chain*/
.then(results => {
// all results available here
});
});
The most effective answer is the one above from #jaromandaX.
Nevertheless, I also found a workaround with Google Chrome, which will help you to not get your hands dirty with promises.
On Chrome:
1. Open Console
2. Go to network tab
3. Near the options "preserve log" and "disable cache", you have an option with an arrow where you will see the label "No throttling".
4.Click on the arrow next to the label, then add.
5. You will be able to set a download and upload speed, and most important, delay between each request.
Kaboom, working with my initial code.
Nevertheless, I changed my code to fit the above answer, which is better to do, in terms of code, speed, etc..
Thanks
(My solution below)
I have several HTML elements with class .canvas-background of which information is stored in the database. I want to get the information of each element and process it via JavaScript. But somehow I can't pass the response of the AJAX request to another function. Here is what I've tried:
function initTabs() {
var tabs = loadTabInformation();
console.log(tabs); // (1)
// do something else
}
function loadTabInformation() {
var requests = new Array();
var tabs = new Object();
var counter = 0;
$(".canvas-background").each(function () {
var tabNumber = $(this).data("tab-number");
var request = $.ajax({
type: 'POST',
url: '../db/GetTabInformation.ashx',
data: String(tabNumber),
dataType: 'json',
contentType: 'text/plain; charset-utf-8'
})
.done(function (response) {
tabs[counter++] = response;
}).fail(function (jqXHR, textStatus, errorThrown) {
console.log("request error in loadTabInformation()");
console.log(textStatus);
console.log(errorThrown);
});
requests.push(request);
});
$.when.apply($, requests).done(function () {
console.log(tabs); // (2)
return tabs;
});
}
At (1) I get undefined, but at (2) everything seems to be alright.
THE SOLUTION:
Thanks to the answer and the link in the comment #Kim Hoang provided I got this working. The clue seemed to put the done() function in the calling function, that is initTabs() in my case. Another thing I got wrong was to try to do the logic that should be executed after the AJAX requests had finished outside the done callback function. They must be inside (makes sense, if you think about it). And a lot of conosle output helped, to see what function returns what kind of object.
function initTabs() {
var tabInfoRequest = loadTabInfo();
tabInfoRequest[0].done(function() {
var results = (tabInfoRequest[1].length > 1) ? $.map(arguments, function(a) { return a[0]; }) : [arguments[0]];
for (var i = 0; i < results.length; i++) {
// do something with results[i]
}
});
}
function loadTabInfo() {
var tabNumbers = new Array();
$(".canvas-background").each(function () {
tabNumbers.push($(this).data("tab-number"));
});
var requests = $.map(tabNumbers, function (current) {
return $.ajax({
type: 'POST',
url: '../db/GetTabInformation.ashx',
data: String(current),
dataType: 'json',
contentType: 'text/plain; charset-utf-8'
});
});
var resultObject = new Object();
resultObject[0] = $.when.apply($, requests);
resultObject[1] = requests;
return resultObject;
}
Note: I only did the resultObject-thing because I needed the array requests in the initTabs() function.
Thank you very much for helping me!
You do not return anything in loadTabInformation, so of course you will get undefined. You should do it like this:
function loadTabInformation() {
...
return $.when.apply($, requests);
}
function initTabs() {
loadTabInformation().done(function (tabs) {
console.log(tabs); // (1)
// do something else
});
}
I need to gather some data from the database through AJAX and place it in an array. Unfortunatly I'm unable to archieve this for some reason.
AJAX sends data to retrieve specific data. This data is as follows:
[{"comment_id":154,"comment_text":"Moduleboeken PHP","date_updated":"2015-06-01 10:34:47"},{"comment_id":155,"comment_text":"Moduleboeken JAVA","date_updated":"2015-06-01 10:34:54"}]
[{"comment_id":149,"comment_text":"Werksfeer","date_updated":"2015-06-01 10:33:57"}]
[{"comment_id":152,"comment_text":"Begeleiding Elleke Jagersma","date_updated":"2015-06-01 10:34:27"}]
[{"comment_id":260,"comment_text":"Studievoortgang JAVA","date_updated":"2015-06-01 13:01:15"}]
[{"comment_id":153,"comment_text":"Faciliteiten","date_updated":"2015-06-01 10:34:39"}]
The function to gather this data:
function sendRetrieveAjax(url, data) {
return new Promise(function(resolve, reject) {
$.ajax({
url: url, type: 'post', data: data,
success: function(data) {
resolve(data);
},
error: function(request, status, error) {
reject([{request: request, status: status, error: error}]);
}
});
});
}
Main code runs through 5 DOM elements, gathers an ID from them and uses this in the AJAX send and retrieve function. If this is succesfull it places it in an array.
var elements = $('.note_block');
var dataCollection = new Array();
for(i = 0; i < elements.length; i++) {
var element = $(elements[i]);
var data = {
commenttype_id : element.children('#commenttype_id').val(),
week_id : $('#week_id').val()
}
sendRetrieveAjax('../functions/getcomments.php', data).then(function(data) {
console.log(data);
dataCollection[i] = data;
});
}
console.log(dataCollection);
The array unfortunatly is empty, while the console shows the correct data.
Can someone enlighten me?
You have two problems
You need to bound the value of i to the sendRetrieveAjax
You need to print the value of dataCollection after filling it (note the use of promise)
To solve the first problem you need to use IIFE (Immediately-Invoked Function Expression)
for(i = 0; i < elements.length; i++) {
var element = $(elements[i]);
var data = {
commenttype_id : element.children('#commenttype_id').val(),
week_id : $('#week_id').val()
}
(function(_i) {
sendRetrieveAjax('../functions/getcomments.php', data).then(function(data) {
console.log(data);
dataCollection[_i] = data;
});
})(i);
}
And to solve the second problem, you can use an array or promises to keep all request's promises in it, and execute them either sequential or parallel
var requests = []
;
for(i = 0; i < elements.length; i++) {
var element = $(elements[i]);
var data = {
commenttype_id : element.children('#commenttype_id').val(),
week_id : $('#week_id').val()
}
// No need to store the URL, just store the data
requests.push(data);
}
requests = requests.map(function(data) {
return sendRetrieveAjax('../functions/getcomments.php', data);
});
Promise.all(requests).done(function(responses) {
console.log(responses);
dataCollection = responses;
}, function(err) {
});
You need to map each individual response to correct array index. The most optimal solution in this case would be to use $.when to provide an array of promises and the get centralized response object with ordered response data objects.
I also simplified sendRetrieveAjax as $.ajax already returns promise object:
function sendRetrieveAjax(url, data) {
return $.ajax({
url: url,
type: 'post',
data: data
});
}
var promises = $('.note_block').map(function(i) {
return sendRetrieveAjax('../functions/getcomments.php', {
commenttype_id: $(this).children('.commenttype_id').val(),
week_id: $('#week_id').val()
});
}).get();
$.when.apply(null, promises).then(function() {
var dataCollection = $.map(arguments, function(data) {
return data[0];
});
console.log('Data Collection', dataCollection);
});
Another thing, don't duplicated ids, use .commenttype_id classes instead.
Here is a demo: http://plnkr.co/edit/r9NnlxIQjUhNvTYwfLy7?p=preview
I am writing a simple app where I need to get the data from a http get api call and store it in Parse for some reason not all the data is stored. Also the http call returns before saving all the data here is the code.
How can I make it to return only after saving all the data?
Parse.Cloud.httpRequest({
url: 'http://api.rottentomatoes.com/api/public/v1.0/lists/movies/upcoming.json?apikey=apikey',
success: function(httpResponse) {
var jsonobj = JSON.parse(httpResponse.text);
var total = jsonobj.movies.length;
for ( var idx in jsonobj.movies) {
var movie = new Movie();
movie.save(new Movie(jsonobj.movies[idx])).then(
function(object){
console.log(object)
},
function(error){
console.log(error);
}
)
}
response.send("All saved");
},
error: function(httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
response.send("Failed");
}
})
You need to aggregate all the promises you used via an aggregation function, in the case of parse promises, it's .when :
Parse.Cloud.httpRequest({
url: 'http://api.rottentomatoes.com/api/public/v1.0/lists/movies/upcoming.json?apikey=apikey',
success: function(httpResponse) {
var jsonobj = JSON.parse(httpResponse.text);
var total = jsonobj.movies.length;
var results = [];
// do NOT iterate arrays with `for... in loops`
for(var i = 0; i < jsonobj.movies.length; i++){
var movie = new Movie();
results.push(movie.save(new Movie(jsonobj.movies[i]))); // add to aggregate
}
// .when waits for all promises
Parse.Promise.when(promises).then(function(data){
response.send("All saved");
});
},
error: function(httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
response.send("Failed");
}
})
Although, it might be better to use promises for the httpRequest too, this should work.
I want to list all my twitter followers in my WinJS.UI.ListView.
WinJS.xhr({
url: "https://api.twitter.com/1/followers/ids.json?cursor=-1&screen_name=" + twitterusername,
responseType: "json"
}).then(
function (xhr) {
var json = JSON.parse(xhr.responseText);
var a = [];
a = json.ids;
//
// now what next?
//
},
function (error) {
myListView.textContent = error;
}
);
I get all my twitter follores id by json.ids.
But next how to find their screen names and prifile pictures and main thing how to bind them with my ListView control. Becouse I had bind simple my static data into ListView but for this example i have no idea.
You have to make another call for each ids to api.twitter.com/1/users/show.json?user_id=json.ids[i].
After you receive all callbacks, you have to create an array with objects that have title, text and picture properties. After that simply bind it with you list.
The following code is an exemple (not tested, don't know if it's functional, but it should point you in the right direction)
var followersCallback = function(xhr){
var json = JSON.parse(xhr.responseText);
var promises = [];
// make promises for each user id (call to twitter to get picture/name/description)
for (var i = 0; i < json.ids.length; i++){
var promise = WinJS.xhr({
url: "https://api.twitter.com/1/users/show.json?user_id=" + json.ids[i],
responseType: "json"
});
promises.push(promise);
}
var dataArray = [];
// join those promises
WinJs.Promise.join(promises)
.then(function(args){
//when you get callback from all those promises
for (var j = 0; j < args.length; j++){
//not sure if parse is needed
args[j]=JSON.parse(args[j].responseText);
//populate your data array
var obj = {};
obj.title = args[j].name;
obj.picture = args[j].profile_image_url;
obj.text = args[j].description;
dataArray.push(obj);
}
//bind your data to the list
var dataList = new WinJS.Binding.List(dataArray);
});
};
WinJS.xhr({
url: "https://api.twitter.com/1/followers/ids.json?cursor=-1&screen_name=" + twitterusername,
responseType: "json"
}).then(
followersCallback,
function (error) {
myListView.textContent = error;
}
);