I have the following function (I'm using the Q promise library):
confirmEmail: function(confirmationCode){
var deferred = q.defer();
User.find({
where: {confirmation_code: confirmationCode}
}).then(function(user){
if(user){
user.updateAttributes({
confirmation_code : null,
confirmed: true
}).then(function() {
deferred.resolve();
});
}else{
deferred.reject(new Error('Invalid confirmation code'));
}
});
return deferred.promise;
}
I've been reading a bit about the best practices regarding promises e.g. What is the explicit promise construction antipattern and how do I avoid it?
http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
Have I written the above function so that it is keeping with these practices, or is there a better way?
It seems to me like you can rewrite your method to this:
confirmEmail : function(confirmationCode) {
return User.find({
where : { confirmation_code : confirmationCode }
}).then(function(user) {
if (! user) {
throw new Error('Invalid confirmation code');
}
return user.updateAttributes({
confirmation_code : null,
confirmed : true
});
});
}
Both User.find() and user.updateAttributes() seem to be returning promises (I'm inferring this from your code), so you can easily create a promise chain with them.
But even if they weren't returning promises, you probably wouldn't have needed q.defer(), as outlined on this page you already mention ("Rookie mistake #4"). See Q.Promise.
With Mongoose (especially from version 4), promises are supported natively, so you don't need to use Q. Also, on Node.js 0.12+ and io.js there's natie support for Promises, so again no need for Q!
This is how I would write the method (using the native Promise; here a polyfill if still on Node 0.10)
confirmEmail: function(confirmationCode){
return new Promise(function(resolve, reject) {
User.find({
where: {confirmation_code: confirmationCode}
})
.then(function(user) {
if(user){
user.updateAttributes({
confirmation_code : null,
confirmed: true
})
.then(resolve, reject)
}
else {
reject(new Error('Invalid confirmation code'))
}
}, reject)
})
}
How it works:
1. The confirmEmail method returns one Promise. As per pattern, the promise can be either resolve()'d or reject()'d
2. User.find is invoked. With Mongoose, that returns a promise, so you can do: then(callback, reject). So, if User.find returns an error, the Promise returned by confirmEmail will be rejected (the "outer one").
3. If User.find succeeds, we proceed. If there is no result (if(user) is false), then we manually reject the "outer promise" and we don't do anything else.
4. If there's a user, instead, we call user.updateAttributes, which too returns a promise. We're thus invoking then(resolve, reject) so the result of that promise will be passed to the "outer promise".
Example use:
obj.confirmEmail(confirmationCode).
.then(function(user) {
// Everything went fine
}, function(error) {
// This is invoked if: User.find() failed, or if no result was obtained (with the error "Invalid confirmation code") or if user.updateAttributes failed
})
Related
I'm in the process of building a small Library that relies heavily on asynchronicity and I'm trying to support NodeJs callback style, Promises and async/await with minimal duplication of functional code.
So, as example take the following ( it doesn't matters what it does)
class Someclass{
constructor(){}
asyncMethod(someData, cb){
var commonVar = ''
if (cb) { // ******* Callback Requested
doSomething()
var result=usingCallbacks()
if (!result) cb('Error')
cb(null, result)
} else{ // ******** we should use a promise
return new Promise((resolve,reject) => {
doSomething()
var result=usingPromises()
if (!result) reject(err)
resolve(result)
})
}
}
}
So i'm stuck on how to build the async/awit part here. Any ideas?
Nothing else is required since await can await anything that returns a Promise (or something promise-like).
In other words, this should "just work":
async useLibFn() {
await someclass.asyncMethod('foo');
}
Under the covers, async/await are just syntactic sugar for promises.
async causes a function to always return a promise. Basically equivalent to:
function someFn() {
return new Promise(function(resolve, reject) {
try {
// actual fn body
} catch (ex) {
reject(ex);
}
});
}
await takes some expression and resolves it asynchronously. If the value is promisey, it waits for the promise to resolve (calls then()). Otherwise, it resolves with the value on the next tick. Basically equivalent to:
Promise.resolve(rval).then(...)
Ive read plenty of articles on Promises and I feel i'm still missing something. Here is an example of what i'm trying to wrap my head around.
File: ad.js
// Using node.js activedirectory
class AD
{
constuctor(options){
this.ad = new ActiveDirectory(options);
}
isUserValid(user, password){
return new Promise((resolve, reject) =>{
ad.authenticate(user, password, (err, auth) ->{
if(err){
reject({ code: 500, message: 'Unknow error'});
}
if(auth){
resolve({code: 200, message: 'ok'});
}
});
)
}
}
module.exports = {
AD: AD
}
File: session.js
createSession(user, password){
var self = this;
return new Promise((resolve, reject) =>{
const ad = new AD("Required parameters");
Code: 1
const result = self._isADValid(ad, user, password);
Code: 2
self._isADValidPromise(ad, user, password)
.then(resp_ad =>{
})
.catch(err =>{
});
);
}
_isADValidPromise(ad, user, password) {
return new Promise((resolve, reject) => {
ad.isUserValid(user, password)
.then(resp_ad => {
resolv(resp_ad);
})
.catch(err => {
reject(err);
});
});
}
_isADValid(ad, user, password) {
return ad.isUserValid(user, password)
.then(resp_ad => {
return resp_ad;
})
.catch(err => {
return err;
});
}
What i'm trying to understand is the following:
Shouldnt "Code 1" return a value from _isADValid. Instead its returning "Promise{ pending }". I though that you only need to use then/catch from a Promise?
So the function _isADValid() is calling the AD function isUserValid which is returning from a Promise and _isADValid() is wrapped in a then/catch which just returns the result.
Code 2 using "_isADValidPromise" works without any issues. And I get that its wrapped in a Promise so its doing what I expected.
If anyone can help clarify why Code 1 is not working
You have to remember that Promises don't resolve until (at least) the next tick (details here). You're expecting the Promise to return synchronously, but Promises are by definition asynchronous.
Your example demonstrates this: If you think about it, your call to authenticate is very likely asynchronous - reaching out to some server to authenticate the user. Promises were invented to wrap asynchronous operations such as this one in a way that provides a nice control flow and avoids callback hell. So in short, any time you use a Promise, it will return asynchronously.
In that light, self._isADValid(ad, user, password); returns a Promise, so naturally, result is a Promise. The only way to get the result out of it is to add a .then() and wait for it to resolve, which will always be at least one tick into the future.
Hope that helps.
I'm using phantomjs with phridge for correct sharing pages for single page app.
Here's some code example:
if (isbot(req.headers['user-agent'])){
var url= req.protocol+'://'+req.get('host')+ req.originalUrl
phridge.spawn()
.then(function (phantom) {
var page = phantom.createPage();
return page.run(url, function (url, resolve, reject) {
var page = this;
page.open(url, function (status) {
// handle page after load
});
})
.then(function (contnt) {
res.send(contnt);
})
.then(phridge.disposeAll());
.catch(function (err) {
console.error(err.stack);
})
}
else {
next();
}
The question is - how the mechanic res.send() works with promise? Will be phridge.disposeAll() performed?
You are making numerous mistakes. You should make sure you are familiar with Promise style programming before writing these code. See the last section.
In this case, no, because the
.then(function (contnt) {
res.send(contnt);
})
part is not returning a Promise.
In this part, if you are sure res.send will not raise any exception, you could write:
.then(function (contnt) {
res.send(contnt);
return new Promise()
})
And the later part,
.then(phridge.disposeAll())
Is also problematic, you should modify it to be
.then(() => phridge.disposeAll())
even if it is the end of the chain and there is no use of create a new Promise, you should write it this way, because then() function takes functions, not result, to be its argument.
And you need to make sure each .then() branch returns a Promise like object while you are chaining them. (I did not check the others since I don't know what they returns.)
OK, there are more errors, I have seen redundant ; after then() branch. I am not sure if there are more problems.
I think the problem is more serious: you are not understanding Promise style programming. You should read the ES6 Promise docs, or Promise library (like bluebird, depends on which library your library depends on) docs closely first.
I would combine the res.send and disposeAll(). No need to over complicate the code.
res.send is synchronous and returns a boolean.
phridge.spawn()
.then(function (phantom) {
var page = phantom.createPage();
return page.run(url, function (url, resolve, reject) {
var page = this;
page.open(url, function (status) {
// handle page after load
});
})
.then(function (contnt) {
res.send(contnt);
phridge.disposeAll()
})
.catch(function (err) {
console.error(err.stack);
})
I am currently trying to refactor the codebase that I have and want to have a more developer friendly codebase. Part 1 of that is changing callback to Promises. Currently, at some places we are using Async.waterfall to flatten the callback hell, which works for me. The remaining places where we couldn't was because they were conditional callbacks, That means different callback function inside if and else
if(x){
call_this_callback()
}else{
call_other_callback()
}
Now I use bluebird for promises in Node.JS and I can't figure out how to handle conditional callback to flatten the callback hell.
EDIT
A more realistic scenario, considering I didn't get the crux of the issue.
var promise = Collection1.find({
condn: true
}).exec()
promise.then(function(val) {
if(val){
return gotoStep2();
}else{
return createItem();
}
})
.then(function (res){
//I don't know which response I am getting Is it the promise of gotoStep2
//or from the createItem because in both the different database is going
//to be called. How do I handle this
})
There is no magic, you can easily chain promises with return inside a promise.
var promise = Collection1.find({
condn: true
}).exec();
//first approach
promise.then(function(val) {
if(val){
return gotoStep2()
.then(function(result) {
//handle result from gotoStep2() here
});
}else{
return createItem()
.then(function(result) {
//handle result from createItem() here
});
}
});
//second approach
promise.then(function(val) {
return new Promise(function() {
if(val){
return gotoStep2()
} else {
return createItem();
}
}).then(function(result) {
if (val) {
//this is result from gotoStep2();
} else {
//this is result from createItem();
}
});
});
//third approach
promise.then(function(val) {
if(val){
return gotoStep2(); //assume return array
} else {
return createItem(); //assume return object
}
}).then(function(result) {
//validate the result if it has own status or type
if (Array.isArray(result)) {
//returned from gotoStep2()
} else {
//returned from createItem()
}
//you can have other validation or status checking based on your results
});
Edit: Update sample code because author updated his sample code.
Edit: Added 3rd approach to help you understand promise chain
Here is the answer to the promise branch: nested and unnested.
Make the branches and don't join them together.
What does the bookshelf.js tap function do. I didn't find any entry in documentation
return new Library({name: 'Old Books'})
.save(null, {transacting: t})
.tap(function(model) {
//code here
}
http://bookshelfjs.org/#Bookshelf-subsection-methods
Bookshelf uses Bluebird for their promises, and I believe .tap() is one of their specific Promise methods. Looks like it allows you to essentially call a .then() without altering the value being passed through the chain.
http://bluebirdjs.com/docs/api/tap.html
Here is an example of the difference between Promise#tap() and Promise#then(). Note that Promise#tap() is not standard, and is Bluebird-specific.
var Promise = require('bluebird');
function getUser() {
return new Promise(function(resolve, reject) {
var user = {
_id: 12345,
username: 'test',
email: 'test#test.com'
};
resolve(user);
});
}
getUser()
.then(function(user) {
// do something with `user`
console.log('user in then #1:', user);
// make sure we return `user` from `#then()`,
// so it becomes available to the next promise method
return user;
})
.tap(function(user) {
console.log('user in tap:', user);
// note that we are NOT returning `user` here,
// because we don't need to with `#tap()`
})
.then(function(user) {
// and that `user` is still available here,
// thanks to using `#tap()`
console.log('user in then #2:', user);
})
.then(function(user) {
// note that `user` here will be `undefined`,
// because we didn't return it from the previous `#then()`
console.log('user in then #3:', user);
});
According to Reg “Raganwald” Braithwaite,
tap is a traditional name borrowed from various Unix shell commands.
It takes a value and returns a function that always returns the value,
but if you pass it a function, it executes the function for
side-effects. [source]
Here is the same question being posed regarding underscore.js.
The gist is this: all tap does is return the object it was passed. However, if it is passed a function, it will execute that function. So, it is useful for debugging or for executing side-effects within an existing chain without altering that chain.