This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 7 years ago.
Im trying to make a simple comment system using React. I'm saving the comments in Parse. The problem is, when I retrieve the comments from Parse, I need to update the state of my component, but when I try to do it, I get an error "Uncaught ReferenceError: this.setState is not defined".
Not working code
loadComments() {
let Comment = Parse.Object.extend("Comment");
let query = new Parse.Query(Comment);
query.limit(15).find({
success: function(result) {
let data = [];
for (var i = 0; i < result.length; i++) {
var object = result[i];
data.push(object.toJSON());
}
this.setState({ data: data });
}
});
}
If I change my code it works, but I think that should have a better approach to this.
Working code
loadComments() {
let Comment = Parse.Object.extend("Comment");
let query = new Parse.Query(Comment);
let _this = this;
query.limit(15).find({
success: function(result) {
let data = [];
for (var i = 0; i < result.length; i++) {
var object = result[i];
data.push(object.toJSON());
}
_this.setState({ data: data });
}
});
}
You have two other options:
Function.prototype.bind:
success: function (result) {
// etc
}.bind(this)
An arrow function (requires es6), which uses the this from the surrounding scope:
success: (result) => {
// etc
}
Related
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 12 months ago.
function addIDS() {
const imdbIDs = [];
imdbIDs.push("id1");
imdbIDs.push("id2");
...
return imdbIDs ;
}
function getThemoviedbIDs(IDs) {
let arr = [];
let urls = [];
for(let i = 0; i < IDs.length; i++) {
urls.push(`...${imdb_ids[i]}...`);
}
$.each(urls, (i, u) => {
$(function() {
$.ajax({
url: u,
dataType: 'json',
success: function(data) {
arr.push(data.tv_results[0].id);
},
statusCode: {
404: function() {
alert('Err');
}
}
});
});
});
return arr;
}
function getDetails(themoviedbID) {
console.log(themoviedbID);
console.log(`...${themoviedbID[0]}`);
}
const imdbIDs = addIDS();
console.log("IMDB IDs: ", imdbIDs);
const themoviedbIDs = getThemoviedbIDs(imdbIDs);
console.log("themoviedbIDs: ", themoviedbIDs);
const themoviedbIDs = getThemoviedbIDs(themoviedbIDs);
console.log("themoviedbIDs: ", themoviedbIDs);
I am using themoviedb database.
populate() I add imdb IDs to the imdbIDs array, then I get themoviedb IDs by getThemoviedbIDs() function.
In the getDetails() function I would like to make ajax request just like in the getThemoviedbIDs() function, but unfortunatelly there is a problem in there. console.log(themoviedbID) output the appropriate array, but console.log(...${themoviedbID[0]}) output .../undefined...
I can get enough information about the series if I am using themoviedb ID.
This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 1 year ago.
Any idea why the following return an empty array? I cannot understand or debug this error. Initially, storearray1 returns array with values in it. Subsequent usage, storearray1 returns an empty array. If possible can help provide a solution to this problem. I am confused.
function f1(){
var storearray1=[];
for (var skip = 0; skip < 9000; skip = skip + 500) {
$.ajax({
url: "/api/routes?$skip=" + skip,
success: function(results) {
//Set result to a variable for writing
var objs = JSON.stringify(results);
var routetimeobjs = JSON.parse(objs);
storearray1.push(routetimeobjs['value']);
}
});
}
}
function showresult(){
console.log(storearray1);//work (refer to image)
var actualarray=storearray1;
console.log(actualarray); //does not work- return empty array
}
showresult();
Console Log for storearray1:
Try this.
var storearray1=[];
var calls = [];
for (var skip = 0; skip < 9000; skip = skip + 500) {
(function(skp) {
var c = $.ajax({
url: "/api/routes?$skip=" + skp,
success: function(results) {
//Set result to a variable for writing
var objs = JSON.stringify(results);
var routetimeobjs = JSON.parse(objs);
storearray1.push(routetimeobjs['value']);
}
});
calls.push(c);
})(skip);
}
$.when(...calls).then(() => console.log(storearray1));
What I am trying to do here are:
Remove all contents in a class first, because every day the events.json file will be updated. I have my first question here: is there a better way to remove all contents from a database class on Parse?
Then I will send a request to get the events.json and store "name" and "id" of the result into a 2D array.
Then I will send multiple requests to get json files of each "name" and "id" pairs.
Finally, I will store the event detail into database. (one event per row) But now my code will terminate before it downloaded the json files.
Code:
function newLst(results) {
var event = Parse.Object.extend("event");
for (var i = 0; i < results.length; i++){
Parse.Cloud.httpRequest({
url: 'https://api.example.com/events/'+ results[i].name +'/'+ results[i].id +'.json',
success: function(newLst) {
var newJson = JSON.parse(newLst.text);
var newEvent = new event();
newEvent.set("eventId",newJson.data.id);
newEvent.set("eventName",newJson.data.title);
newEvent.save(null, {
success: function(newEvent) {
alert('New object created with objectId: ' + newEvent.id);
},
error: function(newEvent, error) {
alert('Failed to create new object, with error code: ' + error.message);
}
});
},
error: function(newLst) {
}
});
}
};
Parse.Cloud.job("getevent", function(request, status) {
var event = Parse.Object.extend("event");
var query = new Parse.Query(event);
query.notEqualTo("objectId", "lol");
query.limit(1000);
query.find({
success: function(results) {
for (var i = 0; i < results.length; i++) {
var myObject = results[i];
myObject.destroy({
success: function(myObject) {
},
error: function(myObject, error) {
}
});
}
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
var params = { url: 'https://api.example.com/events.json'};
Parse.Cloud.httpRequest(params).then(function(httpResponse) {
var results = [];
var jsonobj = JSON.parse(httpResponse.text);
for (var i = 0; i < jsonobj.data.length; i++) {
var tmp2D = {"name":"id"}
tmp2D.name = [jsonobj.data[i].name];
tmp2D.id = [jsonobj.data[i].id];
results.push(tmp2D);
}
newLst(results);
}).then(function() {
status.success("run job");
}, function(error) {
status.error(error);
});
});
I think my original answer is correct as a standalone. Rather than make it unreadable with the additional code, here it is made very specific to your edit.
The key is to eliminate passed callback functions. Everything below uses promises. Another key idea is decompose the activities into logical chunks.
A couple of caveats: (1) There's a lot of code there, and the chances that either your code is mistaken or mine is are still high, but this should communicate the gist of a better design. (2) We're doing enough work in these functions that we might bump into a parse-imposed timeout. Start out by testing all this with small counts.
Start with your question about destroying all instances of class...
// return a promise to destroy all instances of the "event" class
function destroyEvents() {
// is your event class really named with lowercase? uppercase is conventional
var query = new Parse.Query("event");
query.notEqualTo("objectId", "lol"); // doing this because the OP code did it. not sure why
query.limit(1000);
return query.find().then(function(results) {
return Parse.Object.destroyAll(results);
});
}
Next, get remote events and format them as simple JSON. See the comment. I'm pretty sure your idea of a "2D array" was ill-advised, but I may be misunderstanding your data...
// return a promise to fetch remote events and format them as an array of objects
//
// note - this differs from the OP data. this will evaluate to:
// [ { "name":"someName0", id:"someId0" }, { "name":"someName1", id:"someId1" }, ...]
//
// original code was producing:
// [ { "name":["someName0"], id:["someId0"] }, { "name":["someName1"], id:["someId1"] }, ...]
//
function fetchRemoteEvents() {
var params = { url: 'https://api.example.com/events.json'};
return Parse.Cloud.httpRequest(params).then(function(httpResponse) {
var results = [];
var remoteEvents = JSON.parse(httpResponse.text).data;
for (var i = 0; i < remoteEvents.length; i++) {
var remoteEvent = { "name": remoteEvents[i].name, "id": remoteEvents[i].id };
results.push(remoteEvent);
}
return results;
});
}
Please double check all of my work above regarding the format (e.g. response.text, JSON.parse().data, etc).
Its too easy to get confused when you mix callbacks and promises, and even worse when you're generating promises in a loop. Here again, we break out a simple operation, to create a single parse.com object based on one of the single remote events we got in the function above...
// return a promise to create a new native event based on a remoteEvent
function nativeEventFromRemoteEvent(remoteEvent) {
var url = 'https://api.example.com/events/'+ remoteEvent.name +'/'+ remoteEvent.id +'.json';
return Parse.Cloud.httpRequest({ url:url }).then(function(response) {
var eventDetail = JSON.parse(response.text).data;
var Event = Parse.Object.extend("event");
var event = new Event();
event.set("eventId", eventDetail.id);
event.set("eventName", eventDetail.title);
return event.save();
});
}
Finally, we can bring it together in a job that is simple to read, certain to do things in the desired order, and certain to call success() when (and only when) it finishes successfully...
// the parse job removes all events, fetches remote data that describe events
// then builds events from those descriptions
Parse.Cloud.job("getevent", function(request, status) {
destroyEvents().then(function() {
return fetchRemoteEvents();
}).then(function(remoteEvents) {
var newEventPromises = [];
for (var i = 0; i < remoteEvents.length; i++) {
var remoteEvent = remoteEvents[i];
newEventPromises.push(nativeEventFromRemoteEvent(remoteEvent));
}
return Parse.Promise.when(newEventPromises);
}).then(function() {
status.success("run job");
}, function(error) {
status.error(error);
});
});
The posted code does just one http request so there's no need for an array of promises or the invocation of Promise.when(). The rest of what might be happening is obscured by mixing the callback parameters to httpRequest with the promises and the assignment inside the push.
Here's a clarified rewrite:
Parse.Cloud.job("getevent", function(request, status) {
var promises = [];
var params = { url: 'https://api.example.com'};
Parse.Cloud.httpRequest(params).then(function(httpResponse) {
var results = [];
var jsonobj = JSON.parse(httpResponse.text);
for (var i = 0; i < jsonobj.data.length; i++) {
// some code
}
}).then(function() {
status.success("run job");
}, function(error) {
status.error(error);
});
});
But there's a very strong caveat here: this works only if ("// some code") that appears in your original post doesn't itself try to do any asynch work, database or otherwise.
Lets say you do need to do asynch work in that loop. Move that work to a promise-returning function collect those in an array, and then use Promise.when(). e.g....
// return a promise to look up some object, change it and save it...
function findChangeSave(someJSON) {
var query = new Parse.Query("SomeClass");
query.equalTo("someAttribute", someJSON.lookupAttribute);
return query.first().then(function(object) {
object.set("someOtherAttribute", someJSON.otherAttribute);
return object.save();
});
}
Then, in your loop...
var jsonobj = JSON.parse(httpResponse.text);
var promises = [];
for (var i = 0; i < jsonobj.data.length; i++) {
// some code, which is really:
var someJSON = jsonobj.data[i];
promises.push(findChangeSave(someJSON));
}
return Parse.Promise.when(promises);
This may sound like a really simply/stupid question but I need to ask it as I haven't came across this scenario before... okay I have a service in my angularJS app. this service currently contains 4 methods that all perform 80% the same functionality/code and I wish to make this more efficient. Here is what my service looks like (with a lot of code removed):
.factory('townDataService', function ($http) {
var townList = {};
townList.getTownList = function () {
return $http({method: 'GET', url: '/api/country/cities'})
.then(function (response) {
// HERE WE FORMAT THE response as desired... that creates a returnArray
var returnArray = [];
// loop through the countries
var JsonData = response.data;
for (key in JsonData['countries']) {
// formatting code...
}
// end of repeated CODE
return returnArray; // this is array, we don't do any formatting here
});
};
townList.getCurrentTown = function (place) {
return $http({method: 'GET', url: '/api/country/cities'})
.then(function (response) {
// HERE WE FORMAT THE response as desired... that creates a returnArray
var returnArray = [];
// loop through the countries
var JsonData = response.data;
for (key in JsonData['countries']) {
// formatting code...
}
// end of repeated code
// now the format further / work with the returnArray...
for (var i = 0; i < returnArray.length; i++) {
// do stuff
}
return currentTown; // this is a string
});
};
townList.getCurrentCountry = function (place) {
return $http({method: 'GET', url: '/api/country/cities'})
.then(function (response) {
// HERE WE FORMAT THE response as desired... that creates a returnArray
var returnArray = [];
// loop through the countries
var JsonData = response.data;
for (key in JsonData['countries']) {
// formatting code...
}
// end of repeated code
// now the format further / work with the returnArray...
for (var i = 0; i < returnArray.length; i++) {
// do stuff
}
return currentCountry; // this is a string
});
};
return townList;
}
)
;
Now I repeat the same $http 'GET' in each method and the same formatting code (which is a lot of nested loops) before returning a object array or a string. This is far from efficent! What is the best way to put this functionality into it's own function so we only call the GET url once but still return a promise with each method? Should I set the results of the $http({method: 'GET', url: '/api/country/cities'}) as a var and inject / pass it into each method before formatting the data if necessary? Should I use some sort of $cacheFactory?
Sorry if this is a dumb question and if I haven't explained myself well I shall rephrase the questions.
Thanks in advance.
It is just as you say; this code can (and should) be refactored in many ways. One example:
Let us factor the HTTP stuff into a separate service, that will also take care of caching. (Another idea for this would be to have a service for the HTTP/remote calls and another - maybe a general use decorator - to handle caching. LEt us not go into so much detail for now.) And let us put the formatting code in another method:
The remote call service:
.service('townHttpService', function($http, $q) {
var cache;
function getCities() {
var d = $q.defer();
if( cache ) {
d.resolve(cache);
}
else {
$http({method: 'GET', url: '/api/country/cities'}).then(
function success(response) {
cache = response.data;
d.resolve(cache);
},
function failure(reason) {
d.reject(reason);
}
});
}
return d.promise;
}
function clearCache() {
cache = null;
}
return {
getCities: getCities,
clearCache: clearCache
};
})
The formatter:
.service('townFormatter', function() {
return function townFormatter(jsonData) {
// HERE WE FORMAT THE response as desired... that creates a returnArray
var returnArray = [], key;
// loop through the countries
for (key in jsonData['countries']) {
// formatting code...
}
// end of repeated CODE
return returnArray; // this is array, we don't do any formatting here
};
})
Your townDataService, written in terms of the above:
.factory('townDataService', function (townHttpService, townFormatter) {
var townList = {};
townList.getTownList = function () {
return townHttpService.getCities().then(townFormatter);
};
townList.getCurrentTown = function (place) {
return townHttpService.getCities().then(townFormatter).then(function(cityList) {
var currentTown;
for (var i = 0; i < cityList.length; i++) {
// do stuff
}
return currentTown; // this is a string
});
};
townList.getCurrentCountry = function (place) {
return townHttpService.getCities().then(townFormatter).then(function(cityList) {
var currentCountry;
for (var i = 0; i < cityList.length; i++) {
// do stuff
}
return currentCountry; // this is a string
});
return townList;
})
I guess you got two questions there removing repeated logic and best way to cache results.
First - Removing duplicate code:
Looks like townList.getTownList is the common method, the other two methods is an extension of this method.
So,
townList.getCurrentTown = function(place) {
var towns = townList.getTownList();
for (var i = 0; i < returnArray.length; i++) { //additional stuff
}
return currentTown;
};
townList.getCurrentCountry = function(place) {
var towns = townList.getTownList();
for (var i = 0; i < returnArray.length; i++) { //additional stuff
}
return currentCountry;
};
Second - caching values
Now the call is being http made only in townList.getTownList, the logic to cache can be easily implemented here. But this depends on whether the data will be onetime fetch or can be refreshed.
One time fetch: just enable the cache in the http call $http({method: 'GET', url: '/api/country/cities', cache:true});
Refresh based on request: I would pass an refresh variable to inform whether data has to be refreshed or not. So if the refresh is true or the townList is empty the data will be fetched.
var srvc = this;
var townList;
townList.getTownList = function(refresh ) {
if (refresh || !townList) {
srvc.townList = $http({
method: 'GET',
url: '/api/country/cities'
})
.then(function(response) {
var returnArray = [];
var JsonData = response.data;
for (var key in JsonData.countries) {}
return returnArray; // this is array, we don't do any formatting here
});
}
return townList;
};
There is nothing special that you can do and receive some considerable benefit. You would definitely need to cache your GET response and refactor a bit to avoid code duplication and improve readability:
.factory('townDataService', function ($http) {
var getCitiesAsync = function(){
return $http({method: 'GET', url: '/api/country/cities', cache:true});
};
var townList = {};
townList.getTownList = function () {
return getCitiesAsync().then(prepareTownList);
};
var prepareTownList = function(response){
//extract towns and do whatever you need
return result;
};
...
As for using $cacheFactory - seems like an overhead for such a simple scenario, just use built-in cache option.
To avoid timing issues, it is perhaps good to extend the solution a little bit:
function getCities() {
var d = $q.defer();
if( cache ) {
d.resolve(cache);
}
else {
$http({method: 'GET', url: '/api/country/cities'}).then(
function success(response) {
if (!cache) {
cache = response.data;
}
d.resolve(cache);
},
function failure(reason) {
d.reject(reason);
}
});
}
return d.promise;
}
After a (perhaps second or third) call to the webservice succeeds, one checks if the cache variable was set while waiting for server response. If so, we can return the already assigned value. That way, there will be no new assignment to the variable cache if multiple calls were issued:
function success(response) {
if (!cache) {
cache = response.data;
It does not have to make problems but if you rely on having identical objects (for example when working with data binding) it is great to be sure to only fetch data once!
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 10 years ago.
I have this code
var children;
$.ajax({
url: Routing.generate('snmp_ajax_get_children', {dev: root}),
async: true, type: "GET"
}).done(function(data) {
var children = Array();
for(var i in data) {
children[i] = data[i].split('|');
for (var j in data[i]) {
children[i][j] = $.trim(data[i][j]);
}
}
localStorage.setItem('children', children);
});
children = localStorage.getItem(children);
localStorage.removeItem('children');
I use localStorage (ugly, i know) to get data from callback, because any other approach wasn't work for me (i don't know why), any suggestion?
Since you work with async ajax, you can't request the result of the response until done is really done. To achieve something like this, you could do:
// receiving data
function getData( callback ) {
$.ajax({
url: Routing.generate('snmp_ajax_get_children', {dev: root}),
async: true, type: "GET"
}).done(function(data) {
// is async, so it takes some time until this is triggered...
// I don't know your response but I think children should be
// an object:
// var children = {};
var children = Array();
for(var i in data) {
children[i] = data[i].split('|');
for (var j in data[i]) {
children[i][j] = $.trim(data[i][j]);
}
}
// calling your data handler with the data
callback( children );
});
}
// your data handler
function handleData( data ) {
// do whatever
}
// call the action, setting the callback
getData ( handleData );
Because 'children' variable defines 2 times.
as you know, function has its own context for local variables.
and if you define a variable with var, it belongs to function context
(even if you use same name...)
}).done(function(data) {
var children = Array();
// ...
In this situation, children variable will closed when end of callback.
let's remove 2nd var syntax for accessing outer 'children'.
}).done(function(data) {
children = Array();
// ...