Im trying to send a push message to everyone with read access every time a new note is saved. This is the code I've been trialing. Before I was experience a can't serialise objects this was because I was using asynchronous calls. After doing a bit more research I stumbled across javascript promises. I've never used something like this before so i'm finding it difficult to get my head around it.
The problem I'm finding is that object.getACL() is an asynchronous call. I tried first just adding a .then after it but this returned method not found. Now I've tried to create it as its own function and manually make a new promise and resolve the promise after it returns. This isn't working either. I just need the function to sequential call one after another, like normal programming lol.
In pseudocode it should get the ACL. Evaluate each member in the ACL and return an array of all users with read access. Then send a push notification to each member. Any help would be greatly appreciated.
Parse.Cloud.afterSave("Notes", function(request) {
var idsToSend = [];
var i = 0;
console.log("start");
var getACL = function(object) {
var promise = new Parse.Promise();
console.log("enter getACL");
var noteACL = object.getACL() {
promise.resolve(noteACL);
};
return promise;
};
getACL(request.object).then(function(objACL) {
var ACLinJSON = objACL.toJSON();
for (var key in ACLinJSON) {
if (ACLinJSON[key].read == "true") {
idsToSend[i] = key.id;
console.log("i = " + i + " = " + idsToSend[i]);
i++;
}
}
console.log(idsToSend);
//lookup installations
var query = new Parse.Query(Parse.Installation);
query.containedIn('user', idsToSend);
Parse.Push.send({
where: query,
data: {
alert: "note updated"
}
}, {
success: function() {
console.log("Success sent push");
},
error: function(error) {
console.error("can’t find user"); //error
}
});
});
});
ParseObject.getACL() is not an async call, it returns a Parse.ACL not a Parse.Promise, why bother using a promise for this? You're needlessly complicating your code.
Remove the Promises, just get the ACL and loop through the JSON, and if you still have problems, post a new question with the issues you then have.
Related
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
Update:
The problem below is trying to persist data using an asynchronous call... very inefficient and unreliable. I looked into socket.io but it seems to be ideal for messaging systems. Can any one advise of a better solution/library? The problem is, suppose I am trying to make a connection with a device, that device is streaming data every .8 sec. The data comes as a JSON object, with values timestamped. If I wanted to chart these values and create a seemingly real-time feel, what should I be considering? What I came across in my research:
socket.io
rabitQt
MQTT
Old solution below, asynchronous call is a mess, it has nothing to do with knowing JavaScript callback functions or not, I believe this is just not the way.This is why this is not quite a duplicate question to "How do I return the response from an asynchronous call? " it is not a simple callback or promise.
I'm trying to access my JSON object, so I can map out the data and manipulate it. However, I can't seem to be able to access it outside of my request function. I put my request function inside a variable and tried to call it by calling the parent's properties, but in my command line I read "undefined" what am I doing wrong?
1) I tried using the push() method inside my request function, so I could create some prototypical object that stores all the data, then contain it within a function so I could use it outside my request, but I get the error ".push() is not a function"
var r = request(options, function (error, response, xyz) {
var deviceData = JSON.parse(xyz);
if (error) throw new Error(error);
for (var i = 0; deviceData.data.length > i; i++) {
var extractedData = [];
extractedData = deviceData.data[i];
};
//this executes an entire JSON object with all the data I need
//console.log(extractedData);
});
//my console shows "undefined"
console.log(r.extractedData);
I expect to get a whole JSON object like I do when I console.log inside the request function. Instead, I get "undefined"
UPDATED
I tried using a promise as #ajaykumar has suggested, I ran into the issues described
var xyz = {};
var r = {};
Getdata().then(data => {
r["extractedData"] = data;
//console shows all the data as inside the promise block
//console.log(data)
}, err => console.log(err));
function Getdata() {
return new Promise((resolve, reject) => {
request(options, function (error, response) {
var deviceData = JSON.parse(response.body);
//console.log(xyz) at this point shows all data, message: call successful" and the data looks like this:
[console.log(xyz)][1]
//response shows the entire object too, but it is through Node.js, so you actually see my object contained by the "body" property of Node.js. I didn't think about this before. So now I modified my code and removed xyz as it is actually not necessary
if (error) reject(error);
var extractedData = [];
for (var i = 0; deviceData.data.length > i; i++) {
extractedData.push(deviceData.data[i]);
};
//the console shows all my data
//console.log(extractedData)
resolve(extractedData);
});
//the console no longer associates extractedData, displays "extractedData is undefined"
//console.log(extractedData)
});
}
//console prompts error "ReferenceError: data is not defined", if I try extractedData instead it gives me the same old message "undefined". If I try r[0] I get "undefined"
//console.log(data);
Since request will execute in asynchronous way, it will not wait for request to complete so it will execute the next line before we get response from request hence it is undefined.
Var r = {}
request(options, function (error, response, xyz) {
var deviceData = JSON.parse(xyz);
if (error) throw new Error(error);
for (var i = 0; deviceData.data.length > i; i++) { var extractedData = []; extractedData = deviceData.data[i]; };
r["extractedData"] = extractedData;
console.log(extractedData);
});
// this will execute before request gets complete hence it is undefined
console.log(r.extractedData);
So create promise
Getdata() {
return new promise ((resolve, reject) =>{
request(options, function (error, response, xyz) {
var deviceData = JSON.parse(xyz);
if (error) reject (error) ;
var extractedData = [];
for (var i = 0; deviceData.data.length > i; i++)
{
extractedData.push(deviceData.data[i]);
};
resolve (extractedData)
});
}
}
Call this function
Var r ={}
Getdata ().then(data=>{
r["extractedData"] = data;
},err=>console.log(err))
This will work for your problem
function getHiddenTable(){
var url = ".......";
var apptoken = "blahblah";
return $.get(url, {
act: "API_DoQuery",
clist: "7.8.6",
data: apptoken
});
}
function getRprTable(){
var url = "........";
var apptoken = "blahblah";
return $.get(url, {
act: "API_DoQuery",
clist: "3.60",
data: apptoken
});
}
function main(){
getHiddenTable().then(function(xml_hidden){
hiddenTable = xml_hidden;
console.dirxml(hiddenTable);
return getRprTable();
}).then(function(xml_rpr){
rprTable = xml_rpr;
console.dirxml(rprTable);
});
}
main();
getHiddenTable() returns a $.get request and getRprTable() also returns a $.get request
Both of these functions return two separate xml files that contain different data. I assign both xml files to two separate global variables so they have scope to other function that I need to use them in. However, both console statements display the initial request from getHiddenTable(). But why? And what can I do to fix this?
Update
Hey guys, thanks for the help. I found out my problem with much frustration and actually utilized $.when, from the person who provided the idea in the comments.
$.when(getHiddenTable(), getRprTable()).done(function(xml_hidden, xml_rpr){
console.dirxml(xml_hidden[0]);
console.dirxml(xml_rpr[0]);
});
Accessing the data value of the callback function objects can be done by simply accessing the 0th index.
Thanks
You'll likely want to use Promise.all() to run multiple promises at once and return the results of all the promises once they have all finished.
Example:
Promise.all([getHiddenTable(), getRprTable()])
.then(function(results) {
let hiddenTable = results[0];
let rprTable = results[1];
// do stuff with your results
});
Hope that helps
I'm trying to figure out Promises with Parse.
What I want to do is get a lot of jobs, and then perform an update for each job.
var queryJobs = new Parse.Query("Jobs");
queryJobs.find().then(function (results) {
// Loop thorugh all jobs
for (var i = 0; i < results.length; i++) {
var job = results[i];
// Here. I want to run an update on all object and then continue.
}
return ????;
}).then(function () {
status.success("Finish");
}, function () {
status.error("Error");
});
I tried this without luck. The push block is never executed.
var queryJobs = new Parse.Query("Jobs");
queryJobs.find().then(function (results) {
var promises = [];
// Loop thorugh all jobs
for (var i = 0; i < results.length; i++) {
var job = results[i];
promises.push((function () {
// This is never executed.
var promise = new Parse.Promise();
var query = new Parse.Query("Jobs");
query.first({
success: function (object) {
// ... Do something here....
promise.resolve();
},
error: function () {
promise.resolve();
}
});
promise.resolve();
return promise;
}));
}
return Parse.Promise.when(promises);
}).then(function () {
status.success("Finish");
}, function () {
status.error("Error");
});
Thanks in advance
UPDATE
I've changed the code, and I get into the callback, however, the query is not executed.
...
promises.push((function () {
// GET HERE
var promise = new Parse.Promise();
var query = new Parse.Query("Jobs");
query.first({
success: function (object) {
console.log("CALLBACK");
promise.resolve();
},
error: function () {
console.log("CALLBACK");
promise.resolve();
}
});
}()));
return Parse.Promise.when(promises);
Here is how I would set this up:
var failure = new Parse.Promise();
var success = new Parse.Promise();
var queryJobs = new Parse.Query("Jobs");
queryJobs.each
(
function( job )
{
//Do your changes to the job
return job.save().then
(
function( job )
{
return Parse.Promise.as( "job saved" );
},
function( error )
{
failure.reject("There was an error trying to save a job: " + error.message);
return failure;
}
);
}
).then
(
function( results )
{
success.resolve("Successfully updated all the jobs" )
return success;
},
function( error )
{
failure.reject("There was an error trying to query for Jobs: " + error.message);
return failure;
}
).then
(
function( success )
{
response.success( success );
},
function( failure )
{
response.error( failiure );
}
);
This may not work out of the box, but it has a few key features that may help you.
1) I know that one of the perks that is mentioned in the blog post and what not announces promises is that you can get rid of pyramid code, but if you want descriptive error messages, the pyramid code is a necessary evil. My first promise (queryJobs.each in this case) always has two .then()'s. The second one always just does response.error( failure ) and response.success( success ).
2) I create two promises, although you can use just one. I prefer two so it is clear where I'm failing / succeeding. I return these where I reach a dead end/ the finish line.
3) I used query.each instead of query.find. query.find() is limited to 1000 results, which, while it will probably be more than enough for a long time, will eventually cause you to hit your limit, and you'd need to start paginating your results. Using query.each will perform your function on every single object that could be returned by the query. One perk of query.each vs query.find and iterating through the results is that query.each performs it's callback on each object asynchronously, rather than a linear iteration.
4) In this case it would probably be better just to have return job.save() inside of the each block, but I wanted to show how I do the nested promise returns. This is what allows me to have very specific success / error statements. This is important because even if one link in your promise chain fails, you will keep executing the next links. The exception to this is if a promise is rejected and you don't have an error function until your last chain. The error will get passed from link to link until it finds an error function, which is fine, except it limits how much you can customize your error messages.
I'll also note that what you have is probably going to return the same object again and again for that query.first() method, rather than working with the specific job from the first query. Like, you are iterating through your jobs, but instead of doing anything with each job, you're getting the first job and doing something with it again and again. I don't think that's what you actually wanted, but maybe this is meant to be a "learn promises" post rather than something functional.
Anyway, hope I helped a bit. Let me know if you have questions and I'll do my best to answer them.
edit: I know my style varies greatly from others'. I like opening and closing brackets on a new line, for the most part. I actually read in javascript that this can sometimes cause errors. I forget the specific cases, but this is not one of them. But feel free to edit the style back to how you prefer it.
You have to add promises to promises, not functions. You need to call the function so that it returns the promise:
promises.push((function () {
// ...
}()));
// ^^
Furthermore you have to remove the promise.resolve(); call before the return statement. The promise should only be resolved after the query succeeded. As it currently is, the promise is resolved immediately.
I understand using promises in simple scenarios but currently really confused on how to implement something when using a for loop and some updates to local sqlite database.
Code is as follows
surveyDataLayer.getSurveysToUpload().then(function(surveys) {
var q = $q.defer();
for (var item in surveys) {
var survey = surveys[item];
// created as a closure so i can pass in the current item due to async process
(function(survey) {
ajaxserviceAPI.postSurvey(survey).then(function(response) {
//from response update local database
surveyDataLayer.setLocalSurveyServerId(survey, response.result).then(function() {
q.resolve; // resolve promise - tried only doing this when last record also
})
});
})(survey) //pass in current survey used to pass in item into closure
}
return q.promise;
}).then(function() {
alert('Done'); // This never gets run
});
Any help or assistance would be appreciated. I'm probably struggling on how best to do async calls within loop which does another async call to update and then continue once completed.
at least promises have got me out of callback hell.
Cheers
This answer will get you laid at JS conferences (no guarantees though)
surveyDataLayer.getSurveysToUpload().then(function(surveys) {
return Promise.all(Object.keys(surveys).map(function(key) {
var survey = surveys[key];
return ajaxserviceAPI.postSurvey(survey).then(function(response){
return surveyDataLayer.setLocalSurveyServerId(survey, response.result);
});
}));
}).then(function() {
alert('Done');
});
This should work (explanations in comments):
surveyDataLayer.getSurveysToUpload().then(function(surveys) {
// array to store promises
var promises = [];
for (var item in surveys) {
var survey = surveys[item];
// created as a closure so i can pass in the current item due to async process
(function(survey) {
var promise = ajaxserviceAPI.postSurvey(survey).then(function(response){
//returning this promise (I hope it's a promise) will replace the promise created by *then*
return surveyDataLayer.setLocalSurveyServerId(survey, response.result);
});
promises.push(promise);
})(survey); //pass in current survey used to pass in item into closure
}
// wait for all promises to resolve. If one fails nothing resolves.
return $q.all(promises);
}).then(function() {
alert('Done');
});
Awesome tutorial: http://ponyfoo.com/articles/es6-promises-in-depth
You basically want to wait for all of them to finish before resolving getSurveysToUpload, yes? In that case, you can return $q.all() in your getSurveysToUpload().then()
For example (not guaranteed working code, but you should get an idea):
surveyDataLayer.getSurveysToUpload().then(function(surveys) {
var promises = [];
// This type of loop will not work in older IEs, if that's of any consideration to you
for (var item in surveys) {
var survey = surveys[item];
promises.push(ajaxserviceAPI.postSurvey(survey));
}
var allPromise = $q.all(promises)
.then(function(responses) {
// Again, we want to wait for the completion of all setLocalSurveyServerId calls
var promises = [];
for (var index = 0; index < responses.length; index++) {
var response = responses[index];
promises.push(surveyDataLayer.setLocalSurveyServerId(survey, response.result));
}
return $q.all(promises);
});
return allPromise;
}).then(function() {
alert('Done'); // This never gets run
});
I think I have got my head around Parse promise chains, but what I don't understand is how I return my data from the functions (i) back up the promise chain and (ii) back to the calling method of my original JS code.
Using the code below, the first Parse query is called, then a() and finally b() which is all fine. The console.log at each stage are for test purposes and show that the chains have been executed and in order.
I now have 3 questions:
How do I get the data back from function a() and function b() so I can access it in the main function getUserCompetitionTokens()?
How do I return any data from getUserCompetitionTokens() to the main program which has called this Parse code?
What if I want the data from function a() and also from function b() to BOTH be returned to my main program?
function getUserCompetitionTokens(comp_id) {
Parse.initialize("****","****");
currentUser = Parse.User.current();
user_competition = Parse.Object.extend("UserCompetition");
var user_comp_query = new Parse.Query(user_competition);
user_comp_query.equalTo("UserParent", currentUser);
user_comp_query.find().then(a).then(b);
function a(user_comp_results) {
var no_results = user_comp_results.length;
var id = user_comp_results[0].id;
console.log("User Competition Output: " + no_results + " results found, first item id: " + id);
var Competition = Parse.Object.extend("Competition");
var query = new Parse.Query(Competition);
return query.get(comp_id, {
success: function(competition) {
console.log("COMP - " + competition.id);
},
error: function(competition, error) {
console.log(error);
}
});
}
function b(competition) {
var Competition = Parse.Object.extend("Competition");
var query = new Parse.Query(Competition);
query.get(comp_id, {
success: function(competition) {
console.log("COMP 2 - " + competition.id);
},
error: function(competition, error) {console.log(error);}
});
}
}
You don't return :)
I'm sorry, that is just a joke on a technicality: you see, Promises are a way to express asynchronous behaviour. So you can't, strictly saying, grab the return value of a function.
However, you are already grabbing the results of each step correctly... You just didn't realize you can use it yourself.
The answer is to use your own then handler.
user_comp_query.find().then(a).then(b).then(function(results){
console.log(results); // Hooray!
});
However, keep in mind that a Promise might fail. That's why it's important to pass a second handler, which will be called whenever there is an error.
var handleSuccess = function (results) {};
var handleFailure = function (error) {};
var parsePromise = user_comp_query.find().then(a).then(b);
parsePromise.then(handleSuccess, handleFailure);