So, I have a code which is something like this.
getSomethingAsync(something)
.then(doSomethingAsync)
.then(function(d) {
_d = doSomethingSync(d);
return doSomethingAsyncNext(_d);
})
.then(function(val) {
//All done
})
.catch(err_handler);
I want to make it into something like.
getSomethingAsync(something)
.then(doSomethingAsync)
.then(doSomethingSync)
.then(doSomethingAsyncNext)
.then(function(val) {
//All done
})
.catch(err_handler);
Should I just change doSomethingSync which is:
function(data) {
// do a lot of things with data, throw errors for invalid data
return changed_data;
}
to:
function(data) {
// do a lot of things with data, throw errors for invalid data
return new Promise(function(resolve,reject){
resolve(changed_data);
});
}
or:
function(data) {
return new Promise(function(resolve,reject){
// do a lot of things with data, reject for invalid data
resolve(changed_data);
});
}
Should I just change doSomethingSync which is ...
You don't have to change it all. If the return value of the callback is not a promise, it is directly used to resolve the promise returned by .then. The .then callback does not have to return a promise.
You could write
return Promise.resolve(changed_data);
but again, there is no need. return changed_data; will work just as well.
Related
I'm new to Node/Express and am trying to use Promises to executive successive API calls to Apple's CloudKit JS API.
I'm unclear on how to put the functions in sequence and pass their respective return values from one function to the next.
Here's what I have so far:
var CloudKit = require('./setup')
//----
var fetchUserRecord = function(emailConfirmationCode){
var query = { ... }
// Execute the query
CloudKit.publicDB.performQuery(query).then(function (response) {
if(response.hasErrors) {
return Promise.reject(response.errors[0])
}else if(response.records.length == 0){
return Promise.reject('Email activation code not found.')
}else{
return Promise.resolve(response.records[0])
}
})
}
//-----
var saveRecord = function(record){
// Update the record (recordChangeTag required to update)
var updatedRecord = { ... }
CloudKit.publicDB.saveRecords(updatedRecord).then(function(response) {
if(response.hasErrors) {
Promise.reject(response.errors[0])
}else{
Promise.resolve()
}
})
}
//----- Start the Promise Chain Here -----
exports.startActivation = function(emailConfirmationCode){
CloudKit.container.setUpAuth() //<-- This returns a promise
.then(fetchUserRecord) //<-- This is the 1st function above
.then(saveRecord(record)) //<-- This is the 2nd function above
Promise.resolve('Success!')
.catch(function(error){
Promise.reject(error)
})
}
I get an error near the end: .then(saveRecord(record)) and it says record isn't defined. I thought it would somehow get returned from the prior promise.
It seems like this should be simpler than I'm making it, but I'm rather confused. How do I get multiple Promises to chain together like this when each has different resolve/reject outcomes?
There are few issues in the code.
First: you have to pass function to .then() but you actually passes result of function invocation:
.then(saveRecord(record))
Besides saveRecord(record) technically may return a function so it's possible to have such a statement valid it does not seem your case. So you need just
.then(saveRecord)
Another issue is returning nothing from inside saveRecord and fetchUserRecord function as well.
And finally you don't need to return wrappers Promise.resolve from inside .then: you may return just transformed data and it will be passed forward through chaining.
var CloudKit = require('./setup')
//----
var fetchUserRecord = function(emailConfirmationCode){
var query = { ... }
// Execute the query
return CloudKit.publicDB.performQuery(query).then(function (response) {
if(response.hasErrors) {
return Promise.reject(response.errors[0]);
}else if(response.records.length == 0){
return Promise.reject('Email activation code not found.');
}else{
return response.records[0];
}
})
}
//-----
var saveRecord = function(record){
// Update the record (recordChangeTag required to update)
var updatedRecord = { ... }
return CloudKit.publicDB.saveRecords(updatedRecord).then(function(response) {
if(response.hasErrors) {
return Promise.reject(response.errors[0]);
}else{
return Promise.resolve();
}
})
}
//----- Start the Promise Chain Here -----
exports.startActivation = function(emailConfirmationCode){
return CloudKit.container.setUpAuth() //<-- This returns a promise
.then(fetchUserRecord) //<-- This is the 1st function above
.then(saveRecord) //<-- This is the 2nd function above
.catch(function(error){});
}
Don't forget returning transformed data or new promise. Otherwise undefined will be returned to next chained functions.
Since #skyboyer helped me figure out what was going on, I'll mark their answer as the correct one.
I had to tweak things a little since I needed to pass the returned values to subsequent functions in my promise chain. Here's where I ended up:
exports.startActivation = function(emailConfirmationCode){
return new Promise((resolve, reject) => {
CloudKit.container.setUpAuth()
.then(() => {
return fetchUserRecord(emailConfirmationCode)
})
.then((record) => {
resolve(saveRecord(record))
}).catch(function(error){
reject(error)
})
})
}
I think I'm preventing nested queries as much as possible, but I'm honestly not sure. I understand the calls here can all be executed in a single select query, but I did this to simplify the example.
// This example is in TypeScript
// find user
User.find({where:{username:'user'}})
// if found user
.then(function(user) {
return User.find({where:{username:'other_user'}})
// if found other_user
.then(function(other_user) {
// do stuff
return whatever_i_need
}
// if something went wrong, go straight to parent catch
.catch(function(err) {
// do stuff
throw new Error()
}
}
// if previous .then() returned success
.then(function(data) {
return User.find({where:{username:'yet_another_user'}})
// if found yet_another_user
.then(function(yet_another_user) {
// do stuff
return whatever_i_need_again
}
// if something went wrong, go straight to parent catch
.catch(function(err) {
// do stuff
throw new Error()
}
}
// if anything threw an error at any point in time
.catch(function(err) {
// handle the error
}
However, this results in nested promises, which is exactly what promises are meant to prevent. Is this the "max depth" recommended for promises, or am I missing something? Is there a better way to chain queries?
Return the nested promise instead of handling it in the inner blocks to flatten the structure.
User.find({where:{username:'user'}})
.then(function(user) {
if (user) { // if found user
// do stuff
return User.find({where:{username:'other_user'}});
}
throw new Error('user not-found');
})
.then(function(other_user) {
if (other_user) { // if found other_user
// do stuff
return whatever_i_need;
}
throw new Error('other_user not-found');
})
.then(function(data) {
return User.find({where:{username:'yet_another_user'}})
})
.then(function(yet_another_user) {
if (yet_another_user) { // if found yet_another_user
// do stuff
return whatever_i_need_again;
}
throw new Error('yet_another_user not-found');
}
.then(function(data){
// do stuff
})
.catch(function(err) { // if anything threw an error at any point in time
// handle the error
}
Note that a resolved promise means a query is successfully done. That's it all about. A successful query does't guarantee results to be returned. Empty result is a valid outcome of resolved promises.
Note also that the return value from a resolve or reject callback will be wrapped with a resolved promise, and then passed to the next then block, making a meaningful promise chain. Thanks for #Matt's follow-up feedback below regarding this point.
Two points:
Drop .catch(function(err) { throw new Error() }. It does nothing but remove the error message.
You can unnest the inner then calls
So it just should be
User.find({where:{username:'user'}})
.then(function(user) {
return User.find({where:{username:'other_user'}})
})
.then(function(other_user) {
// do stuff
return whatever_i_need
})
// if previous .then() returned success
.then(function(data) {
return User.find({where:{username:'yet_another_user'}})
})
// if found yet_another_user
.then(function(yet_another_user) {
// do stuff
return whatever_i_need_again
})
// if anything threw an error at any point in time
.catch(function(err) {
// handle the error
})
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 having trouble understanding a basic concept of error handling with chaining promises.
In order to learn the rules, I have written a simple example, guessing what the result will be. But unfortunatly it doesn't behave as I though it will.
I have read multiple articles about the subject but perhaps can't I get details because of my poor english language.
Anyway, here is my code :
var promiseStart = $q.when("start");
var promise1 = promiseStart.then(function() {
return Serviceforpromise1.get();
});
var promise2 = promise1.then(function(data1)
{
return Serviceforpromise2.get(data1);
},function(error)
{
return $q.reject();
});
var promiseend = promise2.then(function(data2)
{
return data2;
},function(error)
{
return error;
});
return promiseend;
Well I know that it can be way better coded but it's just for the purpose.
Here is the code of Serviceforpromise1 function :
function Serviceforpromise1()
{
...
return $http.get(*whatever*).then(function (data){
return data;
},function(error)
{
return $q.reject();
});
}
Consider only the case of Serviceforpromise1's failure. A $q.reject is sent back to main chain so I'm waiting the error callback of "promise1 .then(" to be called and it worked as expected. I decided for the example to transfert the error to the "promise2 .then" so in this error callback I added the line return $q.reject();
But it never reached the second error callback (the "promise2 .then" one) and I don't understand why (like Serviceforpromise1, I returned a rejected promise !)
I will be happy to deeply understand what is happening here.
Thanks for your help.
Your understanding is correct, and the problem appears to lie somewhere in the way you are trying to observe this behavior (in something you haven't shown us).
If you return a rejected promise from either a success or error handler in then(), then the promise returned by then() will resolve to a rejected promise. Observe:
angular.module('app', [])
.controller('C', [
'$q',
function ($q) {
var promiseStart = $q.when("start");
var promise1 = promiseStart.then(function (value) {
console.log('Got a value:', value);
return $q.reject('Error!');
});
var promise2 = promise1.then(function (data1) {
return "Got some stuff";
}, function (error) {
console.log("Caught an error:", error);
return $q.reject('New error');
});
var promiseend = promise2.then(function (data2) {
return data2;
}, function (error) {
console.log('Caught an error:', error); // <-- this is logged to the console
return error;
});
return promiseend;
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"></script>
<div ng-app='app' ng-controller='C'></div>
One thing to note here is that in that last handler, you are returning the error variable, and not throwing an exception or returning a rejected promise. So in this case, promiseend will successfully resolve with the value of that error variable.
I have a async function that returns a random student. Now I want a function that returns two unique students- the source of my problems.
getTwoRandom = function(req) {
var deferred = Q.defer();
Q.all([
Student.getRandom(req),
Student.getRandom(req)
])
.then(function(students){
if(students[0]._id !== students[1]._id) { //check unique
deferred.resolve(students);
} else {
//students are the same so try again... this breaks
return getTwoRandom(req);
}
});
return deferred.promise;
};
then further down I have something like this:
getTwoRandom(req).then(function(students) {
//do what I want...
});
The problem is when I do return getTwoRandom(req); the .then() function down the lines doesnt fire... is this returning a different promise that .then() isnt using?
You've over-complicated it quite a bit :)
You can do it like this instead:
getTwoRandom = function(req) {
return Q.all([
Student.getRandom(req),
Student.getRandom(req)
]).then(function(students) {
if(students[0]._id !== students[1]._id) {
return students;
} else {
return getTwoRandom(req);
}
});
};
Now, why does this work? The result of Q.all is always a new promise (no need to create a new deferred). Whatever value you return (ike students) will be wrapped in this new promise. If instead an actual promise is returned (like getTwoRandom(req)), then that promise will be returned. Which sounds like what you wanted to do.