Promise: What happens to other async request when errors are thrown? - javascript

I am using Bluebird promise. This isn't really explicit in the documentation. Suppose the following, assuming all the instances are appropriate promises:
FindSomeDBModel.then(function(model) {
return [
model.getOtherModels(),
aHTTPRequest('https://google.com')
];
}).spread(function(otherModels, httpResponse) {
// some code
}).catch(function(err) {
res.status(500).send(err);
});
If both model.getOtherModels and aHTTPRequest throws an error, what will be inside the err variable in the catch?
Also, what if model.getOtherModels throws an error first, will it cause a response to be sent out to the client or will it wait for aHTTPRequest to complete? Subsequently, aHTTPRequest throws, then what happens?
Can I respond back to the client as soon as one of the request throws an error? Because it is no longer material whether the other response completes and succeeds.

If an error occurs in one of those two promise executions, what will happen is
the error is received in your catch callback
the other promise is unaffected, its execution goes on
the callback you gave to spread isn't called
If both model.getOtherModels and aHTTPRequest throw an error, only the first one will be received by catch, the other one will just be ignored.
The callback you gave to catch is called as soon as possible, when the first error is thrown, your code doesn't wait for the execution of the other one.

If both model.getOtherModels and aHTTPRequest throws an error, what will be inside the err variable in the catch?
An AggregateError would have been optimal but because of compatibility with .all which is specified outside of Bluebird and bluebird is compatibly with - it'll resolve with the first rejection up the chain.
In its own methods that don't need ES6 compatibility bluebird will return all the errors.
Also, what if model.getOtherModels throws an error first, will it cause a response to be sent out to the client or will it wait for aHTTPRequest to complete? Subsequently, aHTTPRequest throws, then what happens?
It'll reject (and immediately enter the catch) as soon as the error is thrown, bluebird has a special case with .spread which checks if the argument passed is an array and calls .all on it - this is a special case, if you .thend it instead of .spreading it that would not have happened.
Can I respond back to the client as soon as one of the request throws an error? Because it is no longer material whether the other response completes and succeeds.
Yes, but consider using typed (or predicate) exceptions to have more meaningful error handling - if an error that's not an OperationalError happened you might want to restart the server.

Related

Handling Errors early in the Promise Chain

I'm trying to find a general way to handle errors on promise chains. In the following snipped I'd like to handle an potential connection-error directly in my connection.js.
connection.js
function getData() {
return fetch(myEndpoint)
.catch(err => handleConnectionError(err)) // redirect to error page or similar
}
app.js
// import connection
connection.getData()
.then(data => handleData(data.foo))
So there are two ways this scenario could play out:
If I catch the error in connection.js without throwing a new one, it will continue the promise chain and handleData() will fail
If I throw an Error again after handling it, the Promise chain wont be executed any further, but then I have an unhandled-promise-rejection error in the console.
So is there actually no better way, than catching and handling the errors everytime I'm using the getData() function somewhere in my app?
The best way to go about this would be to have a final catch to take care of all errors. E.G:
function errorHandler(e) {
if (e instanceof x) {
//handle here
}
}
And then:
fetch(endPoint).then(doSomething).then(doAnotherThing).catch(err => errorHandler(err))
If fetch or any other promise along the chain produces an error, the rest of the then() statements will be skipped and will be catched by the final catch function. From there you will need to make sure that all types of errors that could be thrown by all promises are taken care of.
However, you will need to get rid of the catch in getData as it will cause the rest of the chain to run as usual since the error is taken care of.
Hope that helps
So is there actually no better way, than catching and handling the errors everytime I'm using the getData() function somewhere in my app?
Yes, that's precisely what you should do:
function getData() {
return fetch(myEndpoint)
}
// elsewhere:
connection.getData().then(handleData, handleConnectionError);
You usually don't know whether you always want to "redirect to error page or similar". Maybe at some places in your app, you can handle connection errors by faking response data, and in other places you might want to show an error message instead of triggering a redirect.

Unhandled Promise rejection on try/catch block

Suppose I have an async function
static async update (id, data){
try {
//async function below
_DB.putsomedata()
return true
} catch(e){
//log errors
}
}
And although I don't need the result of _DB.putsomedata(), I still need to know if the update() function finished without errors.
I did some testing and noticed that the result will always return true (thats expected), but in case it fails, the the error is never caught in the catch block, and that raises an unhandled exception.
Can somebody explain the behaviour?
Assuming _DB.putsomedata is an async function, you need to await it. Otherwise its Promise will simply continue in the background, where it eventually fails and produces the error. Your code will have continued on ahead in the meantime and the try..catch block will have long been exited.
putsomedata function is asynchonious, so the return statment in compute just after marking putsomedata as computable (and not launch, as for synchronious function).
putsomedata return probably a promise, which will be resolved after, when cpu will be avaiable.
you can use .promise() .then() .catch() functions

Rejected promises in Protractor/WebDriverJS

WebDriverJS and Protractor itself are entirely based on the concept of promises:
WebDriverJS (and thus, Protractor) APIs are entirely asynchronous. All
functions return promises.
WebDriverJS maintains a queue of pending promises, called the control
flow, to keep execution organized.
And, according to the definition:
A promise is an object that represents a value, or the eventual
computation of a value. Every promise starts in a pending state and
may either be successfully resolved with a value or it may be rejected
to designate an error.
The last part about the promise rejection is something I don't entirely understand and haven't dealt with in Protractor. A common pattern we've seen and written is using then() and providing a function for a successfully resolved promise:
element(by.css("#myid")).getAttribute("value").then(function (value) {
// do smth with the value
});
The Question:
Is it possible that a promise returned by any of the Protractor/WebDriverJS functions would not be successfully resolved and would be rejected? Should we actually worry about it and handle it?
I've experienced a use-case of promise rejection while using browser.wait(). Here is an example:
var EC = protractor.ExpectedConditions;
function isElementVisible() {
var el = element(by.css('#myel'));
// return promise
return browser.wait(EC.visibilityOf(el), 1000)
.then(function success() {
return true; // return if promise resolved
}, function fail() {
return false; // return if promise rejected
});
}
expect(isElementVisible()).toBe(true);
expect(isElementVisible()).toBe(false);
Here, if element is on a page, success will be executed, otherwise, if it is not found when 1 second passes, then fail will be called. My first point is that providing a callback for rejection gives an ability to be consistent with what one should expect. In this case I am kinda sure that promise will always resolve to true or false, so I can build a suite relying on it. If I do not provide a fail callback, then I'll get an Uncaught exception because of timeout, which will still fail my particular spec and still run the rest of the specs. It won't be uncaught by the way, Protractor is gonna catch it, but here I want to bring a second point, that Protractor is considered a tool which you use to write and run your code, and if an exception is caught by Protractor, then this exception has left your code unhandled and your code has a leak. But ... at the same time I do not think that one should waste time to catch everything in tests: if there is no element on a page or click has failed, then a respective spec will obviously fail too, which is fine in most of the cases. Unless you want to use the result of failure to build some code on top of it like it is in my sample.
That is the great thing about promises you are going to get a response, either an response of data or an error message. That extended to a series of promises like Webdriver uses you are going to get an array of responses or a failure response of the first one that fails. How you handle the failed response is up to you I usually just dump it into a log for the console to see what failed. The only thing you need to figure out is do you abort the rest of your tests or do you continue on.
Here is a decent article on the subject as well. http://www.toolsqa.com/selenium-webdriver/exception-handling-selenium-webdriver/
Fyi what you are doing is fine you are just never bothering to catch any of the errors though, I am not sure if that matters to you or not, you could also abstract the call in a function to auto handle the errors for you if you wanted to log them somewhere.

errors not being thrown after promise

i have a weird situation i would like to know how to solve.
in my app errors that happen after functions with promises do net get thrown and the app just stalls.
here an example:
getTenant = (req) ->
deferred = Q.defer()
deferred.resolve('foo') if req.info.referrer
deferred.resolve('bar') if !req.info.referrer
deferred.promise
Routes =[
{
method: 'GET'
path: '/'
handler: (request, reply) ->
getTenant(request).then (tenant) ->
console.log 'tenant', tenant
# here `User` is not defined and doesn't even exist
# why is there no error here?
if !User.isAuthorized(request, tenant)
reply 'not authorized'
else
reply 'authorized'
}
]
after getTenant i call a function on User.
User doesn't exist or is imported but the app gives me no error.
why is that?
of course if i wrap the code in a try/catch i catch the error but thats not the point. i would expect the code to actually break and throw the error.
here the full sample app: https://github.com/aschmid/hapierrortest
thank you,
andreas
The short answer is because you have neglected to include your error handler. Promises always require both success and error handling functions.
This is a fundamental (and generally good) aspect of promises. Inside a promise handler (success or error), any thrown errors do not hit the window. Instead, the promise implementation internally wraps promise handlers in a try block, and upon capture, rejects the promise returned by .then. Why? So that you can choose where and when and how to deal with errors. This is why you can put a single catch(errHandler) (which is sugar for .then(null, errHandler)) at the end of a long promise chain:
somePromise.then(function s1 (val1){
// use val1, return something (e.g. a new promise)
}).then(function s2 (val2){
// use val2, return something (e.g. a new promise)
}).then(function s3 (val3){
// use val3, return something (e.g. a new promise)
}).catch(function e1 (err1){
// handle e1 as you like (console.log, render something on the UI, etc.)
});
In the above chain, an error that occurs at somePromise, s1, s2, or s3 will be handled at e1. This is pretty nice, better than having to handle the potential error explicitly at every step of the chain. You still CAN handle the error at every step if you want, but it's not at all necessary.
Generally with promises you'll handle the error in some way, you don't WANT errors to crash your app after all. But if for some reason you wanted errors to be re-thrown out to the outer scope, you'd have to use a promise library with a final method that allows for re-throwing. For example, .done() in the Q library.
I'll start by saying that I've never used Hapi, but the syntax you have looks fine EXCEPT...
If req.info is null, you'd get an exception thrown. Try this
deferred.resolve('foo') if req.info && req.info.referrer
deferred.resolve('bar') if !req.info || !req.info.referrer

Using Kris Kowal's Q. How should I catch if any errors have been thrown throughout the life of a chained promise?

I'm new to promises, and I just started using Kris Kowal's Q today (I've read about it before and used it a little with Angular.js), and I was doing an Ajax call with jQuery and turning it into a Q promise. I was trying to find a good way to have some sort of catch at the end that would tell me whether this promise was rejected at one point or not so I could tell the user something went wrong along the way.
The idea is I want to extend the idea to work no matter how long my promise and at the end I want a check to see if it failed at anytime during the promise. This is what I ended up writing, but I wouldn't want to throw a new error in each of my fails/catches like this in order to propogate the error down the promise chain. I want something less weird.
function deleteEvent(id){
var url = "sampleURL/deleteEvent?id=" + id;
return Q($.ajax({
type: 'POST',
url: url,
dataType: 'json'
})).then(function (data) { // success
// on success
UpdateDOMforEventsChanged();
}).fail(function (xhr) { // failure
console.log(xhr);
throw new Error('Problem deleting Event');
});
}
And here's how I extended this promise elsewhere
deleteEvent(event._id).then(function () { // AJAX call was a success delete the things
$('#calendar').fullCalendar('removeEvents', event._id);
}).done(null, function () { // It failed somewhere alert the user.
alert("There was an error with deleting the event. Check your network connection and try again.")
});
So basically I've noticed that if I take out the error that I'm throwing in the fail section of my deleteEvent promise that it executes the following then that comes after it in the extended promise i.e. it executes deleting the events client side from fullCalendar. I don't want it to do this if it fails, how can I fix this? Also is there a reason why it does this? I'm guessing it might be, because you can resolve the error somehow or set a default and continue the promise chain...is that right?
I also notice if I don't throw the error it skips over the fail callback function of the done method. I know this is because the q documentation says the following about it's .done() method,
If there is an unhandled rejection, either because promise is
rejected and no onRejected callback was provided, or because
onFulfilled or onRejected threw an error or returned a rejected
promise, the resulting rejection reason is thrown as an exception in a
future turn of the event loop.
So I'm assuming I can't use .done() to catch all rejections including handled ones, (and I'm guessing in the ajax error is handled since I have the catch/fail after it), but what alternative is there?
A handled rejection is like a caught exception. It stops propagating since well, it was handled. If you want to handle the rejection and keep it rejected you need to rethrow, again, just like in synchronous code.
try {
throw new Error();
} catch(e){
// handle error
}
// no error here this code will keep running.
If you want it to keep rejecting and handle it, you need to rethrow:
try {
throw new Error();
} catch(e){
// handle error
throw e;
}
// this code will not run
The same with promises, just like you wrote. This is not particularly odd about promises, this is how synchronous exceptions work as well. If you want to propagate - you re-throw, otherwise - the errors are considered handled.
If you don't throw in fail it means you recover from error to success (see: Rejection into success [use cursors] mind fail is alias for catch), so result promise ends as successful. It can't be other way
Instead of using fail see if Q provides function which provides access to result and returns self promise (something like aside in implementation I mantain).
Also your flow can be simpler with less then's, see:
function deleteEvent(id) {
return Q($.ajax({
....
})).then(function (data) { // success
// on success
UpdateDOMforEventsChanged();
}); // No need for error handler
};
deleteEvent(event._id).done(function () { // AJAX call was a success delete the things
$('#calendar').fullCalendar('removeEvents', event._id);
}, , function (e) { // It failed somewhere alert the user.
console.error(e.stack || e.message);
alert("There was an error with deleting the event. Check your network connection and try again.")
});

Categories

Resources