I'm trying to reject a promise, and it seems to be working, but not quite as expected.
Parse.Promise.as(1).then(function() {
var vendor = user.get('vendor');
if (vendor)
return vendor.fetch();
else
return Parse.Promise.error("No vendor found");
}, function() {
//specific promise error for this particular promise
res.redirect('/vendor/signup');
}).then(function(result) {
var vendor = result;
res.render('vendor/dashboard.ejs', {
'user': user,
'vendor': vendor
});
}).fail(function(error) {
//general catch all error controller
res.render('error.ejs', {
'error': error
});
});
If the promise fails in the first section where it tries to load the vendor, I want the error for redirect. Instead it's falling through to the ending fail. What's the proper way to do this? Isn't the second function passed to then supposed to be the one that it falls through to if it exists?
The basic signature of .then is .then(onFulfilled,onRejected) - this means it takes the promise it is called on and calls the handlers based on the resolution of that promise:
p.then(function onFulfilled(){
// this gets called only when p fulfills.
},function(){
// this gets called only when p is rejected, if the above onFulfilled
// rejects, this doesn't get called, instead it'll propagate
});
Generally, attach a .then(null,function(){ handler to hand;e exceptions in a code segment:
p.then(function onFulfilled(){
// this gets called only when p fulfills.
}).then(null,function(){ // <- note the .then
// this gets called only when the above promise _including_ the onFulfilled
// handler rejects. Since now the code is `.then`ing the result of the promise
// created by the first .then handler.
});
Also - use .catch in libraries that support it for clarity, unfortunately that's not parse.com promises.
Related
Assume an Express route that makes a call to Mongoose and has to be async so it can await on the mongoose.find(). Also assume we are receiving XML but we have to change it to JSON, and that also needs to be async so I can call await inside of it.
If I do this:
app.post('/ams', async (req, res) => {
try {
xml2js.parseString(xml, async (err, json) => {
if (err) {
throw new XMLException();
}
// assume many more clauses here that can throw exceptions
res.status(200);
res.send("Data saved")
});
} catch(err) {
if (err instanceof XML2JSException) {
res.status(400);
message = "Malformed XML error: " + err;
res.send(message);
}
}
}
The server hangs forever. I'm assuming the async/await means that the server hits a timeout before something concludes.
If I put this:
res.status(200);
res.send("Data saved")
on the line before the catch(), then that is returned, but it is the only thing every returned. The client gets a 200, even if an XMLException is thrown.
I can see the XMLException throw in the console, but I cannot get a 400 to send back. I cannot get anything I that catch block to execute in a way that communicates the response to the client.
Is there a way to do this?
In a nutshell, there is no way to propagate an error from the xml2js.parseString() callback up to the higher code because that parent function has already exited and returned. This is how plain callbacks work with asynchronous code.
To understand the problem here, you have to follow the code flow for xml2js.parseString() in your function. If you instrumented it like this:
app.post('/ams', async (req, res) => {
try {
console.log("1");
xml2js.parseString(xml, async (err, json) => {
console.log("2");
if (err) {
throw new XMLException();
}
// assume many more clauses here that can throw exceptions
res.status(200);
res.send("Data saved")
});
console.log("3");
} catch (err) {
if (err instanceof XML2JSException) {
res.status(400);
message = "Malformed XML error: " + err;
res.send(message);
}
}
console.log("4");
});
Then, you would see this in the logs:
1 // about to call xml2js.parseString()
3 // after the call to xml2js.parseString()
4 // function about to exit
2 // callback called last after function returned
The outer function has finished and returned BEFORE your callback has been called. This is because xml2js.parseString() is asynchronous and non-blocking. That means that calling it just initiates the operation and then it immediately returns and the rest of your function continues to execute. It works in the background and some time later, it posts an event to the Javascript event queue and when the interpreter is done with whatever else it was doing, it will pick up that event and call the callback.
The callback will get called with an almost empty call stack. So, you can't use traditional try/catch exceptions with these plain, asynchronous callbacks. Instead, you must either handle the error inside the callback or call some function from within the callback to handle the error for you.
When you try to throw inside that plain, asynchronous callback, the exception just goes back into the event handler that triggered the completion of the asynchronous operation and no further because there's nothing else on the call stack. Your try/catch you show in your code cannot catch that exception. In fact, no other code can catch that exception - only code within the exception.
This is not a great way to write code, but nodejs survived with it for many years (by not using throw in these circumstances). However, this is why promises were invented and when used with the newer language features async/await, they provide a cleaner way to do things.
And, fortunately in this circumstance xml2js.parseString() has a promise interface already.
So, you can do this:
app.post('/ams', async (req, res) => {
try {
// get the xml data from somewhere
const json = await xml2js.parseString(xml);
// do something with json here
res.send("Data saved");
} catch (err) {
console.log(err);
res.status(400).send("Malformed XML error: " + err.message);
}
});
With the xml2js.parseString() interface, if you do NOT pass it a callback, it will return a promise instead that resolves to the final value or rejects with an error. This is not something all asynchronous interfaces can do, but is fairly common these days if the interface had the older style callback originally and then they want to now support promises. Newer interfaces are generally just built with only promise-based interfaces. Anyway, per the doc, this interface will return a promise if you don't pass a callback.
You can then use await with that promise that the function returns. If the promise resolves, the await will retrieve the resolved value of the promise. If the promise rejects, because you awaiting the rejection will be caught by the try/catch. FYI, you can also use .then() and .catch() with the promise, but in many cases, async and await are simpler so that's what I've shown here.
So, in this code, if there is invalid XML, then the promise that xml2js.parseString() returns will reject and control flow will go to the catch block where you can handle the error.
If you want to capture only the xml2js.parseString() error separately from other exceptions that could occur elsewhere in your code, you can put a try/catch around just it (though this code didn't show anything else that would likely throw an exception so I didn't add another try/catch). In fact, this form of try/catch can be used pretty much like you would normally use it with synchronous code. You can throw up to a higher level of try/catch too.
A few other notes, many people who first start programming with asynchronous operations try to just put await in front of anything asynchronous and hope that it solves their problem. await only does anything useful when you await a promise so your asynchronous function must return a promise that resolves/rejects when the asynchronous operation is complete for the await to do anything useful.
It is also possible to take a plain callback asynchronous function that does not have a promise interface and wrap a promise interface around it. You pretty much never want to mix promise interface functions with plain callback asynchronous operations because error handling and propagation is a nightmare with a mixed model. So, sometimes you have to "promisify" an older interface so you can use promises with it. In most cases, you can do that with util.promisify() built into the util library in nodejs. Fortunately, since promises and async/await are the modern and easier way to do asynchronous things, most newer asynchronous interfaces in the nodejs world come with promise interfaces already.
You are throwing exceptions inside the callback function. So you cant expect the catch block of the router to receive it.
One way to handle this is by using util.promisify.
try{
const util = require('util');
const parseString = util.promisify(xml2js.parseString);
let json = await parsestring(xml);
}catch(err)
{
...
}
I'm writing an API where I'm having a bit of trouble with the error handling. What I'm unsure about is whether the first code snippet is sufficient or if I should mix it with promises as in the second code snippet. Any help would be much appreciated!
try {
var decoded = jwt.verify(req.params.token, config.keys.secret);
var user = await models.user.findById(decoded.userId);
user.active = true;
await user.save();
res.status(201).json({user, 'stuff': decoded.jti});
} catch (error) {
next(error);
}
Second code snippet:
try {
var decoded = jwt.verify(req.params.token, config.keys.secret);
var user = models.user.findById(decoded.userId).then(() => {
}).catch((error) => {
});
user.active = true;
await user.save().then(() => {
}).catch((error) => {
})
res.status(201).json({user, 'stuff': decoded.jti});
} catch (error) {
next(error);
}
The answer is: it depends.
Catch every error
Makes sense if you want to react differently on every error.
e.g.:
try {
let decoded;
try {
decoded = jwt.verify(req.params.token, config.keys.secret);
} catch (error) {
return response
.status(401)
.json({ error: 'Unauthorized..' });
}
...
However, the code can get quite messy, and you'd want to split the error handling a bit differently (e.g.: do the JWT validation on some pre request hook and allow only valid requests to the handlers and/or do the findById and save part in a service, and throw once per operation).
You might want to throw a 404 if no entity was found with the given ID.
Catch all at once
If you want to react in the same way if a) or b) or c) goes wrong, then the first example looks just fine.
a) var decoded = jwt.verify(req.params.token, config.keys.secret);
b) var user = await models.user.findById(decoded.userId);
user.active = true;
c) await user.save();
res.status(201).json({user, 'stuff': decoded.jti});
I read some articles that suggested the need of a try/catch block for each request. Is there any truth to that?
No, that is not required. try/catch with await works conceptually like try/catch works with regular synchronous exceptions. If you just want to handle all errors in one place and want all your code to just abort to one error handler no matter where the error occurs and don't need to catch one specific error so you can do something special for that particular error, then a single try/catch is all you need.
But, if you need to handle one particular error specifically, perhaps even allowing the rest of the code to continue, then you may need a more local error handler which can be either a local try/catch or a .catch() on the local asynchronous operation that returns a promise.
or if I should mix it with promises as in the second code snippet.
The phrasing of this suggests that you may not quite understand what is going on with await because promises are involved in both your code blocks.
In both your code blocks models.user.findById(decoded.userId); returns a promise. You have two ways you can use that promise.
You can use await with it to "pause" the internal execution of the function until that promise resolves or rejects.
You can use .then() or .catch() to see when the promise resolves or rejects.
Both are using the promise returns from your models.user.findById(decoded.userId); function call. So, your phrasing would have been better to say "or if I should use a local .catch() handler on a specific promise rather than catching all the rejections in one place.
Doing this:
// skip second async operation if there's an error in the first one
async function someFunc() {
try {
let a = await someFunc():
let b = await someFunc2(a);
return b + something;
} catch(e) {
return "";
}
}
Is analogous to chaining your promise with one .catch() handler at the end:
// skip second async operation if there's an error in the first one
function someFunc() {
return someFunc().then(someFunc2).catch(e => "");
}
No matter which async function rejects, the same error handler is applied. If the first one rejects, the second one is not executed as flow goes directly to the error handler. This is perfectly fine IF that's how you want the flow to go when there's an error in the first asynchronous operation.
But, suppose you wanted an error in the first function to be turned into a default value so that the second asynchronous operation is always executed. Then, this flow of control would not be able to accomplish that. Instead, you'd have to capture the first error right at the source so you could supply the default value and continue processing with the second asynchronous operation:
// always run second async operation, supply default value if error in the first
async function someFunc() {
let a;
try {
a = await someFunc():
} catch(e) {
a = myDefaultValue;
}
try {
let b = await someFunc2(a);
return b + something;
} catch(e) {
return "";
}
}
Is analogous to chaining your promise with one .catch() handler at the end:
// always run second async operation, supply default value if error in the first
function someFunc() {
return someFunc()
.catch(err => myDefaultValue)
.then(someFunc2)
.catch(e => "");
}
Note: This is an example that never rejects the promise that someFunc() returns, but rather supplies a default value (empty string in this example) rather than reject to show you the different ways of handling errors in this function. That is certainly not required. In many cases, just returning the rejected promise is the right thing and that caller can then decide what to do with the rejection error.
Maybe I just don't understand promises but I've used this pattern before and never had issues. Using bluebird within node.
I have this function which is getting hit:
function getStores() {
return new Bluebird((resolve, reject) => {
return Api.Util.findNearbyStores(Address,(stores) => {
if (!stores.result) {
console.log('one')
reject('no response');
console.log('two')
}
const status = stores.results.status
})
})
}
then hits both of my logs, continues past the if and throws
'one'
'two'
TypeError: Cannot read property 'Status' of undefined
Basically it keeps going right past the resolve.
My impression was the promise should immediately short circuit on reject and pass the rejection through as the resolution to the promise. Am I misunderstanding this?
Yes, you are misunderstanding this. reject(…) is not syntax (just like resolve(…) isn't either) and does not act like a return statement that exits the function. It's just a normal function call that returns undefined.
You should be using
if (!stores.result) reject(new Error('no response'));
else resolve(stores.results.status);
The "short-circuiting" behaviour of rejections is attributed to promise chains. When you have
getStores().then(…).then(…).catch(err => console.error(err));
then a rejection of the promise returned by getStores() will immediately reject all the promises in the chain and trigger the catch handler, ignoring the callbacks passed to then.
I am using node 8.x. Hence, I have access to all latest features i.e. async/ await etc.
The scenario is similar to following
(Not correct syntax, just for explanation):
createUser()
{
let userAddress = createAddress(); // Aync, returns a promise
User.create(name: 'foo', address: userAddress); // User creation is dependant on address. Also, User.create returns a promise.
}
Basically, User object creation is dependant on the creation of address object. I want the createUser function to execute asynchronously i.e. to return a promise as soon as possible without waiting for address object to be created.
The purpose of this question is not to get things done but to understand what is the best way to solve this kind of issues in async programming.
Few ways that I can think of:
1: Create a new promise object and return right away when entered in createUser function. Resolve it when the user object is created.
2: Make createUser an async function and then return user Promise. Facing issues with this method:
async function createUser()
{
address = await createAddress();
return User.create(name: 'foo', address: userAddress);
}
The problem is function waits for the address before returning the control which I don't want. (Or it does not make any diff as the function is async. Performance is a big criterion in my case)
How to deal with this kind of promise dependancy where you want to return promise of parent object but you parent object is dependant on child object's promise.
Thank you.
You can write your function as-is and then it depends on how it's called. You can't use await because that blocks in the current scope. You can introduce a new scope using .then, though:
async function createUser() {
address = await createAddress();
return User.create({ name: 'foo', address });
}
...
createUser().then(() => { /* handle user creation */ });
// code here will not wait for `createUser` to finish
Make createUser an async function and then return user Promise. The problem is function waits for the address before returning the control which I don't want.
No, it doesn't. An async function does immediately return a promise to its caller. It waits only when executing the code in its body (and resolves the promise when you return). Your code works, this is exactly what you should be doing.
If createAddress is a promise returning the address, let's say address, you can do something like this:
createAddress().then(function(address) {
User.create(name: 'foo', address: address).then(function() {
// User and address has been created
})
})
// Do something here that runs even though createAddress has not yet been resolved
This will require you to wait for the promise to be resolved before doing some other code.
If you want to return a promise, then you're basically looking to use promises the way they were intended to be used:
createUser() {
return createAddress()
.then((address) =>
User.create({ name: 'foo', address: address })
);
}
Now the createUser method is returning a promise, which will be resolved at some point in the future. Your code can continue work, can add more .then(...) or .catch(...) callbacks to that promise, etc.
This is another approach, assuming that createAddress returns a promise (of course)
function createUser(){
return createAddress().then(function(address) {
// the return is to handle latter the user creation
return User.create(name: 'foo', address: address)
}, function() {
throw 'Promise Rejected';
});
}
createUser().then(function(result) {
console.log("result => " + result);
// Do something here with the 'user creation' if you want
}).catch(function(error) {
console.log("error => " + error);
});
// Do some stuff, while the code above is executed
I'm migrating my Mongoose code to use Promises to avoid The Pyramid of Doom.
I want to break the Promise's chain on a certain point, but I don't know how to do it.
Here's my code:
var data = {};
People.find({}).exec()
.then(function(people) {
if (people.length == 0){
// I want to break the chain here, but the console.log() gets executed
res.send('No people');
return;
}
data['people'] = people;
return Events.find({
'city': new mongoose.Types.ObjectId(cityID)
}).lean().exec();
}).then(function(events) {
console.log('here');
data['events'] = events;
res.send(data);
});
You need to reject or throw an error in the handler to "stop" a promise chain from running.
From the Mongoose Docs, you would want to call the #reject method of the promise.
The handler you have for reject should examine the reason and "do the right thing" (like cause a 404 to be returned or an empty array if you're doing RESTful stuff)
If you don't have access to the promise (since you're in the handler already for example), just throw new Error().
This will invoke the rejected handler you provided.