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);
Related
Let's say I have some code that looks like this:
var doSomething = function(parameter){
//send some data to the other function
return when.promise(function(resolveCallback, rejectCallback) {
var other = doAnotherThing(parameter);
//how do I check and make sure that other has resolved
//go out and get more information after the above resolves and display
});
};
var doAnotherThing = function(paramers){
return when.promise(function(resolveCallback, rejectCallback) {
//go to a url and grab some data, then resolve it
var s = "some data I got from the url";
resolveCallback({
data: s
});
});
};
How do I ensure that var other has completely resolved before finishing and resolving the first doSomething() function? I'm still wrapping my head around Nodes Async characteristic
I really didn't know how else to explain this, so I hope this makes sense! Any help is greatly appreciated
EDIT: In this example, I am deleting things from an external resource, then when that is done, going out the external resource and grabbing a fresh list of the items.
UPDATED CODE
var doSomething = function(parameter){
//send some data to the other function
doAnotherThing(parameter).then(function(){
//now we can go out and retrieve the information
});
};
var doAnotherThing = function(paramers){
return when.promise(function(resolveCallback, rejectCallback) {
//go to a url and grab some data, then resolve it
var s = "some data I got from the url";
resolveCallback({
data: s
});
});
};
The return of doAnotherThing appears to be a promise. You can simply chain a then and put your callback to utilize other. then also already returns a promise. You can return that instead.
// Do stuff
function doSomething(){
return doAnotherThing(parameter).then(function(other){
// Do more stuff
return other
});
}
// Usage
doSomething().then(function(other){
// other
});
Below is how to accomplish what you're trying to do with bluebird.
You can use Promise.resolve() and Promise.reject() within any function to return data in a Promise that can be used directly in your promise chain. Essentially, by returning with these methods wrapping your result data, you can make any function usable within a Promise chain.
var Promise = require('bluebird');
var doSomething = function(parameter) {
// Call our Promise returning function
return doAnotherThing()
.then(function(value) {
// Handle value returned by a successful doAnotherThing call
})
.catch(function(err) {
// if doAnotherThing() had a Promise.reject() in it
// then you would handle whatever is returned by it here
});
}
function doAnotherThing(parameter) {
var s = 'some data I got from the url';
// Return s wrapped in a Promise
return Promise.resolve(s);
}
You can use the async module and its waterfall method to chain the functions together:
var async = require('async');
async.waterfall([
function(parameter, callback) {
doSomething(parameter, function(err, other) {
if (err) throw err;
callback(null, other); // callback with null error and `other` object
});
},
function(other, callback) { // pass `other` into next function in chain
doAnotherThing(other, function(err, result) {
if (err) throw err;
callback(null, result);
})
}
], function(err, result) {
if (err) return next(err);
res.send(result); // send the result when the chain completes
});
Makes it a little easier to wrap your head around the series of promises, in my opinion. See the documentation for explanation.
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 have a function called makeNewNode() that should take a Parse GeoPoint object and return a (custom) Parse object called node that contains a GeoPoint object with the key "location" and a pointer to another custom Parse object with the key "stop." What this function should do is essentially "snap" to a node if there is one within the SNAP_RADIUS of its GeoPoint. If not, return a brand new node with a null "stop" pointer and a "location" of the parameter geoPoint.
I have a couple issues with this code.
The first is that it seems to always fail to return anything. The Parse query always returns success, which is good. However, it only returns something usable when it "snaps." Otherwise it just returns an undefined results variable.
The second (and main) is that regardless of snapping or not, the function never returns anything remotely similar to the node object that I'm expecting. This leads me to believe through extensive use of console.log that node is never changed or affected by the actions within the success function of the query.
The third is related to the previous in a more general sense. Every time I've tried to return an object from a function in Javascript it hasn't acted the way I've expected. Every time I've tried to change a variable from within a query's success function nothing actually changes. I am a little new to this more in-depth Javascript as my previous experience has been a little more light.
This is the troublesome code.
function makeNewNode(geoPoint) {
var node = {};
var Node = Parse.Object.extend("Nodes");
var query = new Parse.Query(Node);
query.withinMiles("location",geoPoint,SNAP_RADIUS);
query.first({
success: function(results) {
console.log(results);
console.log(results + "");
if (results == undefined) {
node = new Node();
node.set("location",geoPoint);
node.set("stop",null);
node.save();
console.log(node.id);
} else {
node = results;
}
},
error: function(error) {
console.log("Failed to create a node. Error: " + error.message + ".");
}
});
return node;
}
And here is where I call it.
var geoPoint = new Parse.GeoPoint(location.lat(),location.lng());
var newnode = makeNewNode(geoPoint);
Any ideas or suggestions about my code or either of the three questions is greatly appreciated.
A cleaner way is to use promises, so that the save of the newly created object can be handled without the extra callback parameter and without creating deeper and deeper nesting of success functions. Moreover, the caller may want to make a node as part of some larger set of asynch steps.
A promise version looks like this:
// return a promise that, when fulfilled, creates a new node with
// at the geoPoint, or, if a node exists within SNAP_RADIUS, returns that node
function makeNewNode(geoPoint) {
var Node = Parse.Object.extend("Nodes");
var query = new Parse.Query(Node);
query.withinMiles("location",geoPoint,SNAP_RADIUS);
return query.first().then(function(node) {
if (!node) {
node = new Node();
node.set("location", geoPoint);
node.set("stop", null);
}
return (node.isNew())? node.save() : Parse.Promise.as(node);
});
}
The caller can now chain this with other promises.
EDIT - Here's how you call it. Lets say we get the geoPoint in another asynchronous call, then...
var geoPoint = new Parse.GeoPoint(location.lat(),location.lng());
makeNewNode(geoPoint).then(function(newNode) {
console.log("new node id is " + newNode.id);
}, function(error) {
console.log("error " + error.message);
});
What's up with all the returns? A promise is a placeholder for the result. Returning it gives the caller and object to which they can attach a completion function (using the then() method). That completion callback can also return a promise (that's the inner return), so that they can be chained arbitrarily. The second parameter to then() is an optional callback to handle failure.
I don't know if you are aware of the async issues, but they might be the problem.
function makeNewNode(geoPoint) {
var Node = Parse.Object.extend("Nodes");
var node = new Node();
var query = new Parse.Query(Node);
query.withinMiles("location",geoPoint,SNAP_RADIUS);
query.first({
success: function(results) {
console.log(results);
console.log(results + "");
if (results == undefined) {
node.set("location",geoPoint);
node.set("stop",null);
node.save(function() {
console.log(node.id);
});
} else {
node.set("location", results.get("location"));
node.set("stop", results.get("stop"));
}
},
error: function(error) {
console.log("Failed to create a node. Error: " + error.message + ".");
}
});
return node;
}
query.first() is an async function. When your function returns, the async function may have not called your callback. In this situation, the return value would be the object {} as you defined at the beginning.
Later when you assign a new value(new Node() and so) to node, the returned value would still be the previous object, instead of the new object assigned to node.
The simple fix would be to use a pre-assigned object for returning, and update its content later, just like my example above.
However, this is never a good solution for an async code snippet. You should use either a callback or a promise to return the new Node.
Using a callback would be like:
function makeNewNode(geoPoint, callback) {
var Node = Parse.Object.extend("Nodes");
var node;
var query = new Parse.Query(Node);
query.withinMiles("location",geoPoint,SNAP_RADIUS);
query.first({
success: function(results) {
console.log(results);
console.log(results + "");
if (results == undefined) {
node.set("location", geoPoint);
node.set("stop", null);
node.save(function() {
console.log(node.id);
callback(null, node);
});
} else {
callback(null, results);
}
},
error: function(error) {
console.log("Failed to create a node. Error: " + error.message + ".");
callback(error);
}
});
}
And you would be able to use it like:
makeNewNode(geoPoint, function(err, newNode) {
// use your new Node here.
})
I'm trying to get my head around promises in JavaScript. I feel like I know what a promise is. However, I don't understand how to use them. In an attempt to learn, I decided to query a database from Node.js. In my code, I have one JavaScript file called test.js. Test.js looks like this:
Test.js
'use strict';
module.exports = function (app) {
var customerService = require('customer.js')(app);
var getCustomerTest = function() {
customerService.getCustomer(1).then(
function (customer) { console.log(customer); },
function (error) { console.log(error); }
);
};
};
Customer.js
'use strict';
module.exports = function(app) {
var _ = require('lodash');
var db = require('db');
return {
getCustomer: function(customerID) {
try {
console.log('querying the database...');
var database = db.connect(CONNECTION_STRING);
database.query('select * from customers where [ID]="' + customerID + '", function(error, result, response) {
if (error) {
// trigger promise error
} else {
// This throws an exception because displayMessage can't be found.
this.displayMessage('Success');
// trigger promise success
}
});
} catch (ex) {
// trigger promise error
}
},
displayMessage: function(message) {
console.log(new Date() + ' - ' + message);
}
};
};
I'm struggling trying to setup the promise in getCustomer. Especially since the call to the database call happens asynchronously. I feel like my call to customerService.getCustomer is the correct approach. However, once inside of getCustomer, I have two issues:
How do I setup / return my promise?
Why can't I call displayMessage after the database query is done? How do I do this?
Thank you JavaScript whiz!
"Why can't I call displayMessage after the database query is done"
You are getting the error because this is not referencing the object that contains getCustomer and displayMessage. This is because you are in a callback function and the context has changed.
You need to save a reference to the correct context and use that to access displayMessage
getCustomer: function(customerID) {
//saving the context
var that = this;
try {
...
database.query('select * from customers where [ID]="' + customerID + '", function(error, result, response) {
...
// Now use it here to call displayMessage
that.displayMessage('Success');
...
}
});
} catch (ex) {
...
}
},
"How do I setup / return my promise?"
You will need a promise library (unless you plan to make your own). For this I will show the use of the q library
There are a couple ways of doing this, but I will show the use of deferreds.
The basic process is:
Create a deferred object in a function that will call an async function/method.
Return the promise object
Set the appropriate callbacks for then/fail/fin etc on the promise object.
In the callback of the async function resolve or reject the deferred, passing any arguments that will be needed in the callbacks.
The appropriate callbacks that were set in step 2 will then be called in order.
Code
var Q = require("q");
...
getCustomer:{
var deferred = Q.defer(),
database = db.connect(CONNECTION_STRING);
database.query("some query", function(error, result, response) {
if(error){
//Anything passed will be passed to any fail callbacks
deferred.reject(error);
} else {
//Anything passed will be passed to any success callbacks
deferred.resolve(response);
}
});
//Return the promise
return deferred.promise;
}
...
customerService.getCustomer(1).then(function (customer) {
console.log(customer);
}).fail(function (error) {
console.log(error);
}).done();
The q library has quite a few helpful api functions. Some help with getting promises with Node async functions. Read the Adapting Node section of the readme to see how it is done.
JSFiddle Demo demonstrating in browser use of q
I use promises in my website (still learning) and I would like to know if there is a difference between this:
return promise
.then(ctxTransport.getTransportById(idTran, transport))
.then(checkLocking)
.fail(somethingWrong);
and this:
return promise
.then(function () { return ctxTransport.getTransportById(idTran, transport); })
.then(function () { return checkLocking(); })
.fail(somethingWrong);
With the first implementation sometimes I got errors.
var getTransportById = function (transportId, transportObservable, forceRemote) {
// Input: transportId: the id of the transport to retrieve
// Input: forceRemote: boolean to force the fetch from server
// Output: transportObservable: an observable filled with the transport
...
return manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
function querySucceeded(data) {
transportObservable(data.results[0]);
}
};
function checkLocking() {
var now = new Date();
transport().lockedById(5);
transport().lockedTime(now);
return ctxTransport.saveChanges(SILENTSAVE);
}
function somethingWrong(error) {
var msg = 'Error retreiving data. ' + error.message;
logError(msg, error);
throw error;
}
Thanks.
When passing functions in the promise chain, you're supposed to pass the function names without arguments or the (), or as in the second case, anonymous functions. This is because Q will call it for you with the result/return value of the previous promise resolution.
Therefore, .then(ctxTransport.getTransportById(idTran, transport)) is semantically incorrect, since you're not passing a function, but the return value of ctxTransport.getTransportById.