Difference between new Promise and Promise.resolve/reject in bluebird - javascript

I call storage.createTask and use BlueBird's promises to get the returned value. This works fine when I do new Promise() and resolve the promise using either resolve(something) or reject(error). However, Promise.reject(new Error('some error')) causes cannot read property 'then' of undefined.
According to the docs, Promise.reject
Creates a promise that is rejected with the given reason
. Isn't this similar to reject(error), which works fine?
What's the difference between Promise.resolve/Promise.reject and doing new Promise? When should we use one over the other?
//server.js
// returning Promise.reject causes
// Cannot read property 'then' of undefined
storage.createTask(task).then(function(id) {
task.id = id;
reply(task);
}, function(error) {
console.log(error);
reply(error);
});
// storage.js
function _create(task) {
return new Promise(function(resolve, reject) {
var id = shortid.generate();
Task.create({
id: id,
content: task.content,
deadline: task.deadline,
milestone_id: task.milestone_id,
}).catch(function (error) {
reject(error); // works ok
}).then(function() {
resolve(id); //works ok
});
});
}
module.exports = {
createTask: function(task) {
if (task.milestone_id != null ) {
Milestone.isExist(task.milestone_id).then(function(exists) {
if (!exists) {
return Promise.reject(new Error('some error'));
}
return _create(task);
});
} else {
return _create(task);
}
}

Your createTask() function does not return anything; you need to return the promise created by Milestone.isExist().
Update
Here's how I would rewrite the function:
createTask: function(task) {
if (task.milestone_id == null ) {
return Promise.reject(new Error('null id'));
}
return Milestone.isExist(task.milestone_id).then(function(exists) {
if (!exists) {
return Promise.reject(new Error('some error'));
}
return _create(task);
});
}
With this rewrite, you are always returning a promise from createTask(), so you can safely chain off of it.

Related

Passing data in promise chain nodejs

I have a chain of promises that the first one gets data from an API, and the 2nd one inserts the data into a database.
I am attempting to pass the data from the first promise to the 2nd promise, but it's coming through as undefined.
Here is my code:
var getBalancePromise = function() {
var promise = new Promise(function(resolve, reject) {
poloniexExchange.getBalance({
account: 'all'
}, function(err, response) {
if (err)
console.log(err);
reject(err);
if (!err)
resolve(response); //response is an array
});
}).catch((err) => {
console.log('error');
})
return promise;
};
var updateBalancePromise = function(balanceArray) //balanceArray undefined. This should be the data from the first promise in the chain.
{
var promise = new Promise(function(resolve, reject) {
balanceArray.data.forEach(function(element) {
db.collection('balances').update({
currency: element.currency
}, {
$set: {
amount: element.amount,
shortname: element.shortName
}
}, {
upsert: true
});
});
resolve(true);
console.log('balances updated into database');
});
return promise;
};
getBalancePromise()
.then(updateBalancePromise);
How do I change my code to pass data from first promise to 2nd promise?
You are always rejecting the promise:
if (err)
console.log(err);
reject(err); // This line is always executed
if (!err)
resolve(response); //response is an array
This causes the .catch callback to be triggered (.catch((err) => { console.log('error'); })) which doesn't return anything, so balanceArray is undefined.
First make sure to only reject the promise if there is an error:
if (err) {
console.log(err);
reject(err);
}
Secondly, either rethrow the error in the .catch callback or remove it completely and catch at the top level instead:
getBalancePromise()
.then(updateBalancePromise)
.catch(...);

Unhandled promise rejection Error: Can't set headers after they are sent

I would like to make a if else return (for conrtole) but: "UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Can't set headers after they are sent.
"
exports.delete = function (req, res) {
Parking.findById(req.params.id).exec()
.then(function (parking) {
if (userController.ensureAuthorized(req, 'Director', parking.id)) {
return parking;
}
return res.status(403).send({msg: 'unauthorized'});
})
.then(function (parking) {
User.update().exec();
return parking;
})
.then(function (parking) {
return Parking.remove({_id: parking._id}).exec();
})
.then(function () {
res.status(200).json({msg: 'Ok ! Parkink remove'});
})
.catch(function (err) {
return res.status(400).send(err);
});
};
Ty
The issue is that after return res.status(403), the promise chain doesn't stop automagically. Eventually, it will hit res.status(200) and cause the error.
You can rewrite your promise chain a bit to prevent this. I'm not sure what the purpose of that User.update().exec() is, but I assume that you wanted to call it and also wait for its promise to get resolved before continuing:
exports.delete = function (req, res) {
Parking.findById(req.params.id).exec()
.then(function (parking) {
if (userController.ensureAuthorized(req, 'Director', parking.id)) {
return User.update(...).exec().then(function() {
return Parking.remove({_id: parking._id}).exec();
}).then(function() {
return res.status(200).json({msg: 'Ok ! Parkink remove'});
});
} else {
return res.status(403).send({msg: 'unauthorized'});
}
}).catch(function (err) {
return res.status(400).send(err);
});
};
Well there is no standard way of breaking the promise chain.
So I am going to throw an error to break the chain, and then handle that custom thrown error:
exports.delete = function (req, res) {
Parking.findById(req.params.id).exec()
.then(function (parking) {
if (userController.ensureAuthorized(req, 'Director', parking.id)) {
return parking;
}
else {
res.status(403).send({msg: 'unauthorized'});
throw new Error('BREAK_CHAIN'); // <-- intentionally throw error
}
})
.then(function (parking) {
User.update().exec();
return parking;
})
.then(function (parking) {
return Parking.remove({_id: parking._id}).exec();
})
.then(function () {
res.status(200).json({msg: 'Ok ! Parkink remove'});
})
.catch(function (err) {
if(err.message != 'BREAK_CHAIN') // <-- handle if error was intentionally thrown
return res.status(400).send(err);
});
};
just as an addition to the other answers given, I would recommend:
1) breaking things up into small parts
2) using throw as intended, to raise the error of non-authorization
function update (parking) {
User.update().exec()
.then(function () {
Parking.remove({_id: parking._id}).exec();
});
}
exports.delete = function (req, res) {
// auth needs req so we put it in scope
var auth = function (parking) {
if (!userController.ensureAuthorized(req, 'Director', parking.id)) {
throw(new Error(403));
}
return parking;
}
Parking.findById(req.params.id).exec()
.then(auth)
.then(update)
.then(function () {
res.status(200).json({msg: 'Ok ! Parkink remove'});
})
.catch(function (err) {
if (err.message === 403) {
return res.status(403).send({msg: 'unauthorized'});
}
return res.status(400).send(err);
});
};

Static Promise.resolve()/reject() is always interpreted as resolve()

I'm using the following two pieces of code :
Store.addUser(newUserInfo).then(function(firstResult) {
Store.getUserList().then(function(result){
console.log('this side');
console.log(result);
io.sockets.emit('userAdded', {
userMapByUserId: result
});
}, function(error) {
console.log('List of users could not be retrieved');
console.log(error);
io.sockets.emit('userAdded', {
userMapByUserId: []
});
}
);
}, function(rejection) {
socket.emit('userNotAdded', {
userId: -1,
message: rejection.reason,
infoWithBadInput: rejection.infoWithBadInput
});
});
and in Store :
var addUser = function(newUserInfo) {
var validationResult = Common._validateUserInfo(newUserInfo);
if (validationResult.isOK) {
return keyValueExists('userName', newUserInfo.userName).then(function(userNameAlreadyExists) {
if (userNameAlreadyExists) {
validationResult = {
isOK: false,
reason: 'Username already exists',
infoWithBadInput: 'userName'
};
return Promise.reject(validationResult);
} else {
var newUserId = generateUserId();
//TODO: change it somehting more flexible. e.g. a predefined list of attributes to iterate over
var newUser = {
'userName': newUserInfo.userName,
'password': newUserInfo.password,
'userId': newUserId,
'lastModificationTime': Common.getCurrentFormanttedTime(),
'createdTime': Common.getCurrentFormanttedTime()
};
var user = new User(newUser);
user.save(function(err) {
if (err) {
console.log(err);
console.log('There is a problem saving the user info');
return Promise.reject('There is a problem saving the user info');
} else {
console.log('A new user added: ');
console.log(newUser);
//return getUserList();
return Promise.accept(newUser);
}
});
}
});
} else {
return Promise.reject(validationResult);
}
};
But in the first code , when I do Store.addUser(newUserInfo) it always runs the first function (resolve function) which shouldn't be the case if we do return Promise.reject() in addUser. Any idea on why this happens ?
You've got two return statements too few, two too much, and are overlooking a non-promisified function call.
Store.addUser(newUserInfo).then(function(firstResult) {
return Store.getUserList().then(function(result){
// ^^^^^^
…
This one is not really problematic, as you don't chain anything after the resulting promise, but it shouldn't be missed anyway.
…
return keyValueExists('userName', newUserInfo.userName).then(function(userNameAlreadyExists) {
if (userNameAlreadyExists) {
…
} else {
…
var user = new User(newUser);
user.save(function(err) { … });
// ^^^^
}
});
In this then-callback, you are not returning anything from your else branch. The promise is immediately fulfilled with undefined, and the ongoing save call is ignored - your promises don't know about it, so they can't await it. That's why Store.getUserList() that follows next in the chain doesn't see the changes; they're not yet stored.
It's also the reason why your Promise.reject inside that callback is ignored, and why Promise.accept never caused any problems.
You will need to create a new promise for the result of the save invocation here (so that you actually can return it):
…
var user = new User(newUser);
return new Promise(function(resolve, reject) {
user.save(function(err) {
if (err) {
console.log(err);
console.log('There is a problem saving the user info');
reject('There is a problem saving the user info');
} else {
console.log('A new user added: ');
console.log(newUser);
resolve(newUser);
}
});
}); // .then(getUserList);

Bluebird promises - nesting vs rejecting pattern

I'm working on an app for which we're using promises. I'm attempting to figure out what the better pattern is.
Breaking up concerns between thenables and rejecting the promise if an error. Rejection is then handled in a catch block. Or is it better to throw a new type of error and handle in the catch block?
Account.findOneAsync({email: request.payload.email})
.then(function (user) {
if (user) {
return user.compareHash(request.payload.password);
} else {
// Account not found
return Bpromise.reject('AccountNotFound');
}
})
.then(function (validPassword) {
if (validPassword) {
return request.auth.jwt.user.sign();
} else {
// Invalid password
return Bpromise.reject('InvalidPassword');
}
})
.then(function (jwt) {
var response = reply.success();
return response.header('authorization', jwt);
})
.catch(function (e) {
if (e === 'AccountNotFound' || e === 'Invalid Password') {
return reply(Boom.unauthorized('Invalid username/password'));
} else {
// Perhaps log something like unhandled error
return reply(Boom.unauthorized('Invalid username/password'));
}
});
Or nesting promising as such. I feel here that this is just going down the same rabbit hole of "callback hell" though.
Account.findOneAsync({email: request.payload.email})
.then(function (user) {
if (user) {
user.compareHash(request.payload.password)
.then(function (valid) {
if (valid) {
request.server.plugins.jwt.sign()
.then(function (jwt) {
var response = reply.success();
return response.header('authorization', jwt);
});
} else {
// Invalid password
return reply(Boom.unauthorized('Invalid username/password'));
}
});
} else {
// Account not found
return reply(Boom.unauthorized('Invalid username/password'));
}
})
.catch(function (e) {
console.log(e);
});
I think you can get the best of both worlds by throwing and then catching your boom objects.
One thing you're missing in both approaches is that when you're already inside a then handler, the idiomatic thing to do is throw an error rather than creating and returning a rejected promise. You also don't need an else block after a return statement:
Account.findOneAsync({email: request.payload.email})
.then(function (user) {
if (user) {
return user.compareHash(request.payload.password);
}
// Account not found
throw Boom.unauthorized('Invalid username/password');
})
.then(function (validPassword) {
if (validPassword) {
return request.auth.jwt.user.sign();
}
// Invalid password
throw Boom.unauthorized('Invalid username/password');
})
.then(function (jwt) {
var response = reply.success();
return response.header('authorization', jwt);
})
.catch(function (e) {
if (e.isBoom) {
return reply(e);
}
// Perhaps log something like unhandled error
return reply(Boom.unauthorized('Invalid username/password'));
});

Return another promise from a promise

I have a promise for an object and would like to get a promise for a property of that object. How should I do that?
var user = Q.nfcall(User.findOne, {
_id: userId
});
var accessToken = Q.Promise(function (resolve, reject) {
user.then(function (user) {
if (!user) return reject(new Error('User not found.'));
if (!user.github.accessToken) return reject(new Error('Access token not found.'));
return resolve(user.github.accessToken);
}, function(err) {
return reject(err);
});
});
This is what I tried so far, but I'm not sure if its the best (or most correct) way.
Do not use the deferred antipattern1! There's no need to use Promise constructor, .then already returns you a promise for the result of its callback:
var accessToken = user.then(function(user) {
if (!user) throw new Error('User not found.');
if (!user.github.accessToken) throw new Error('Access token not found.');
return user.github.accessToken;
});
[1]: You've seen yourself how errorprone it is :-)

Categories

Resources