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.
Related
I'm trying to fetch data via post request and the API I'm querying from returns a maximal 50 elements. This worked great so far, but now we have some uses cases where it's mandatory to receive an higher indeterminate number of elements.
The $http-method returns a promise but I must rely on this data to make another request.
My simplified code:
var data =
"q=" + term
"×tamp=" + latest
$http({
method: 'POST',
url: url,
data: data
}).then(
function(response) {
let dataList = response.data.list;
if(dataList.length > 50)
/*TODO: new query with other timestamp-value*/
return dataList ;
},
function(response) {
if(shouldLog){
console.log("[DebuggingService] " + response);
}
}
);
Is there a way to combine multiple http requests depending on the request before?
EDIT
There is not pagination. So it's mandatory to change the timestamp value. (taking the latest timestamp of the response)
I would wrap that request in a function that can also be called recursively inside then() to return another request promise
Something like:
function getData(term, time, res = []) {
var data = "q=" + term + "×tamp=" + time
// return the $http promise
return $http({
method: 'POST',
url: url,
data: data
}).then(function(response) {
let dataList = response.data.list;
// merge new data into final results array
res = res.concat(dataList);
if (dataList.length > 50) {
const timeStampFromResponse = //????
// return new request promise
return getData(term, timeStampFromResponse, res)
}
// return final array
return res;
})
}
getData(term, latest).then(function(results) {
// all results available here
}).catch(function(response) {
if (shouldLog) {
console.log("[DebuggingService] " + response);
}
})
Why not do something like this:
var data =
"q=" + term + "×tamp=" + latest;
var amounts = 2;
url = 'https://api.github.com/search/repositories?q=topic:ruby+topic:rails';
call(url, data, checkForMore);
function call(url, data, callback) {
$http({
method: 'POST',
url: url,
data: data
}).then(function(data){
console.log(data);
console.log('------------');
callback(data);
});
}
function checkForMore(data){
if (amounts > 0){
amounts -= 1;
var newData = "q=" + term +
"×tamp=" + data.SomehowGetLastestTimestamp;
call(url, newData, checkForMore);
} else {
// Save it maybe?
}
}
This is a very rough example, and probably doesn't work but it gets you a good idea what to do.
Basically, have a callback method that is called on the .then anonymous function. Then you can pass in the data and check to see if you need to call it more. (Based on your situations). If you do, then just call the call function again, but update your data.
I currently have a function which makes a httpRequest and parses the json received into an array of urls. I want to fire a second httpRequest after the first request is complete and data is parse, below both solutions I've tried return null.
Solution1
var promises1 = [];
Parse.Cloud.define("FetchData", function(request, response) {
var promises = _.map(urls, function(url) {
return Parse.Cloud.httpRequest({ url:url });
});
Parse.Promise.when(promises).then(function() {
createSearchUrls(arguments)
//Creates an array of urls from request data to be used in second http request
});
//Fire second HTTP request here after urls have been created from first request data
var promises1 = _.map(appTitles, function(appTitles) {
return Parse.Cloud.httpRequest({ url: appTitles});
});
Parse.Promise.when(promises1).then(function() {
//nothing returned
response.success(_.toArray(arguments));
}, function (error) {
response.error("Error: " + error.code + " " + error.message);
});
});
Solution 2 (Using then after createSearchUrl() function
Parse.Cloud.define("FetchData1", function(request, response) {
var promises = _.map(urls, function(url) {
return Parse.Cloud.httpRequest({ url:url });
});
Parse.Promise.when(promises).then(function() {
//Creates an array of urls from request data to be used in second http request
createSearchUrls(arguments).then( function() {
//Fire second HTTP request here after urls have been created from first request data
promises_1 = _.map(appTitles, function(appTitles) {
return Parse.Cloud.httpRequest({ url: appTitles});
});
})
});
Parse.Promise.when(promises_1).then(function() {
//nothing returned
response.success(_.toArray(arguments));
}, function (error) {
response.error("Error: " + error.code + " " + error.message);
});
});
createSearchUrls()
function createSearchUrls(arguments){
for (a = 0; a < arguments.length; a++){
var json = JSON.parse(arguments[a].text);
for (i = 0; i < json.feed.entry.length; i++) {
var urlEncoded = encodeURI(ENCODE JSON DATA);
var finalUrl = 'URL HERE';
appTitles.push(finalUrl);
}
}
return appTitles;
}
It looks like the idea of making a series of httpRequests and collecting the results is something that can and should be factored out....
function manyRequests(urls) {
var promises = _.map(urls, function(url) {
return Parse.Cloud.httpRequest({ url:url });
});
return Parse.Promise.when(promises).then(function() {
return _.toArray(arguments);
});
}
Now its just a matter of calling that twice....
Parse.Cloud.define("FetchData1", function(request, response) {
manyRequests(urls).then(function(results) {
createSearchUrls(results); // assigns to the gobal "appTitles"
return manyRequests(appTitles);
}).then(function(result) {
response.success(result);
}, function(error) {
response.error(error);
});
});
What that says is, call (a globally defined, presumably) list of urls and collect the results. From those results, run a local function to generate another list of urls (assigning those to a global, presumably), call those and return the result to the client.
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);
});
});
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 was wondering what the order of operations are in the parse cloud. I currently am running into trouble trying to do multiple things at once inside my job on the cloud. I am currently trying to make an HTTP request for each user in my user table (there are 2) and then get the webpage or httprequest.text from the webpage. My code is as followed
Parse.Cloud.job("WeatherUpdates2", function(request, status) {
var query = new Parse.Query(Parse.User);
query.exists("City");
query.each(
function(result){
var object = result;
console.log(object.id);
var city = object.get("City");
city = city.replace(" ", "");
city = city.replace(" ", "");
// get the country code.
var countryCode = object.get("CountryCode");
var woeidUrl = "http://where.yahooapis.com/v1/places.q(" + city + "," + countryCode + ")?appid=(appid)";
console.log(woeidUrl);
var woeID = "An error occured retrieving your WOEID.";
Parse.Cloud.run('httpRequest', { url: woeidUrl }, {
success: function(WOEID) {
console.log("returned from http request.");
},
error: function(error) {
console.log("Error occurred while making request for WOEID " + error.message);
status.error(error.message);
}
});
},
{
success: function() {
// results is an array of Parse.Object.
console.log('#Query');
status.success("Updated Records!!");
},
error: function(error) {
// error is an instance of Parse.Error.
console.log('#error');
response.error("Failed to save vote. Error=" + error.message);
}
});
});
Where the job httpRequest is:
Parse.Cloud.define("httpRequest", function(request, response) {
var webpage = "Something went wrong.";
Parse.Cloud.httpRequest({
url: request.params.url,
success: function (httpResponse) {
webpage = httpResponse.text;
webpage = webpage.toString();
response.success(webpage);
},
error: function (error)
{
console.log("Error in http request " + error.message);
response.error(error.message);
}
});
});
now I would expect to be printed would be the, object id of first user, their url, the job running, the message"returned from http request." then repeated another time for the second user and finally the job finishing with the message "Updated Records". but instead I get:
I2014-07-22T15:15:16.013Z] A5hod7qKE3
I2014-07-22T15:15:16.045Z] http:/where.yahooapis.com/v1/places.q(Draper,US)?appid=(appid)
I2014-07-22T15:15:16.053Z] GmuqxpTUpM
I2014-07-22T15:15:16.066Z] http:/where.yahooapis.com/v1/places.q(SaltLakeCity,US)?appid=(appid)
I2014-07-22T15:15:16.082Z] #Query
I2014-07-22T15:15:16.131Z] v385: Ran cloud function httpRequest with:
Input: {"url":"http://where.yahooapis.com/v1/places.q(SaltLakeCity,US)?appid=(appid)"}
Result:
2487610TownSalt Lake CityUnited StatesUtahSalt LakeSalt Lake City40.777561-111.93071740.699890-112.10125740.852951-111.739479511America/Denver
I2014-07-22T15:15:16.141Z] v385: Ran cloud function httpRequest with:
Input: {"url":"'http://where.yahooapis.com/v1/places.q(Draper,US)?appid=(appid)'"}
Result:
http://where.yahooapis.com/v1/schema.rng'" xmlns:yahoo="http://www.yahooapis.com/v1/base.rng" yahoo:start="0" yahoo:count="1" yahoo:total="11">2393601TownDraperUnited StatesUtahDraper8402040.524139-111.86627240.442921-111.92212740.544361-111.78304349America/Denver
I removed 1 / from both the printing urls so I could posts this because I don't have high enough rep to post more than 2 links. I also have put in my appid into the (appid) so the url does return to me the according woeid from yahoo.com. The problem here being I can't actually get into the success function of the http request job. Any help is greatly appreciated!
EDIT:
I was trying to figure out how to run a job in a for loop but couldn't get it to work. I tried to make a promise and do what Fosco said below, but have had no luck. Here is my code.
for(var i = 0; i < woeIDs.length; i++)
{
console.log("hello");
var weatherURL = "http://weather.yahooapis.com/forecastrss?w=" + woeIDs[i] + "&u=f";
var promise = Parse.Cloud.run('httpRequest', { url: weatherURL }, {
success: function(WOEID) {
console.log("got here");
},
error: function(error) {
console.log("Error occurred while making request for WOEID " + error.message);
status.error(error.message);
}
});
Parse.Promise.when([promise]).then(function() { status.success(); });
}
if I run this code I get a hello twice then the two job calls then the "got here" message once. I have tried adding a return statement to it and with no luck also. Thanks again for all the help!!!
The issue here is that inside the each callback, you need to return the promise from your cloud function call if you want to ensure the tasks complete, and have it wait before going to the next object.
Simplified and using promises:
query.each(function(object) {
...
return Parse.Cloud.run(...);
}).then(function() {
// success
}, function(err) {
// error
});
For looping over a promise-returning function like Parse.Cloud.run:
var promises = [];
for (i = 0; i < 5; i++) {
promises.push(Parse.Cloud.run('...', {}));
}
Parse.Promise.when(promises).then(function() {
// all done
}, function(err) {
// error
});