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
Related
My question consist of two parts:
Part 1
According to standard ES6 Promise I see that I forced to use catch block everywhere, but it looks like copy/paste and looks weird.
Example:
I have some class which makes request to backend (lets call it API class).
And I have a few requirements for API class using:
1) I need to make requests in different parts of my application with single request errors processing:
// somewhere...
api.getUser().then(... some logic ...);
// somewhere in another module and in another part of app...
api.getUser().then(... some another logic...);
2) I want so 'then' blocks would work ONLY when 'getUsers' succeeded.
3) I don't want to write catch block everywhere I use api.getUsers()
api.getUser()
// I don't want following
.catch(e => {
showAlert('request failed');
})
So I'm trying to implement single error processing inside of the class for all "users requests"
class API {
getUser() {
let promise = makeRequestToGetUser();
promise.catch(e => {
showAlert('request failed');
});
return promise;
}
}
...but if request fails I still forced to use catch block
api.getUser()
.then(... some logic...)
.catch(() => {}) // <- I forced to write it to avoid of “Uncaught (in promise)” warning
... otherwise I'll get “Uncaught (in promise)” warning in console. So I don't know the way of how to avoid of .catch block everywhere I use api instance.
Seems this comes from throwing error in such code:
// This cause "Uncaught error"
Promise.reject('some value').then(() => {});
May be you can say 'just return in your class "catched" promise'.
class API {
getUser() {
return makeRequestToGetUser().catch(e => {
showAlert('request failed');
return ...
});
}
}
...but this contradict to my #2 requirement.
See this demo: https://stackblitz.com/edit/promises-catch-block-question
So my 1st question is how to implement described logic without writing catch block everywhere I use api call?
Part 2
I checked if the same API class implementation with Q library will get the same result and was surprised because I don't get “Uncaught (in promise)” warning. BTW it is more expectable behavior than behavior of native ES6 Promises.
In this page https://promisesaplus.com/implementations I found that Q library is implementation of Promises/A+ spec. But why does it have different behavior?
Does es6 promise respects Promises/A+ spec?
Can anybody explain why these libraries has different behavior, which one is correct, and how implement mentioned logic in case if "ES6 Promises implementation" is correct?
I see that I forced to use catch block everywhere
No, you don't need to do that. Instead, return the promise created by then to the caller (and to that caller's caller, and...). Handle errors at the uppermost level available (for instance, the event handler that started the call sequence).
If that's still too many catchs for you, you can hook the unhandledrejection event and prevent its default:
window.addEventListener('unhandledrejection', event => {
event.preventDefault();
// You can use `event.reason` here for the rejection reason, and
// `event.promise` for the promise that was rejected
console.log(`Suppressed the rejection '${event.reason.message}'`);
});
Promise.reject(new Error("Something went wrong"));
The browser will trigger that event prior to reporting the unhandled rejection in the console.
Node.js supports this as well, on the process object:
process.on('unhandledRejection', error => {
// `error` is the rejection reason
});
Note that you get the reason directly rather than as a property of an event object.
So I don't know the way of how to avoid of .catch block everywhere I use api instance.
Surely the caller of getUser needs to know it failed? I mean, if the answer to that is really "no, they don't" then the event is the way to go, but really the code using api should look like this:
function useTheAPI() {
return getUser()
.then(user => {
// Do something with user
});
}
(or the async equivalent) so that the code calling useTheAPI knows that an error occurred; again, only the top-level needs to actually handle the error.
Can anybody explain why these libraries has different behavior, which one is correct, and how implement mentioned logic in case if "ES6 Promises implementation" is correct?
Both are correct. Reporting unhandled exceptions entirely in userland (where libraries live) is hard-to-impossible to do such that there aren't false positives. JavaScript engines can do it as part of their garbage collection (e.g.: if nothing has a reference to the promise anymore, and it was rejected, and nothing handled that rejection, issue the warning).
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.
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.
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.")
});
Can anybody tell me how to deal with unhandled exceptions in WinJS code. Is there are better way to handle them instead of using try/catch block.I have already made use of try/catch block in some parts of my code.
try/catch is the language mechanism to handle exceptions.
Are you dealing with regular exceptions, or do you have an unhandled exception in asynchronous code (inside a promise)? If the latter, try/catch won't work, because the stack frame that set up the try/catch is gone by the time the async operation completes.
In that case, you need to add an error handler to your promise:
doSomethingAsync().then(
function (result) { /* successful completion code here */ },
function (err) { /* exception handler here */ });
Exceptions propagate along promise chains, so you can put a single handler at the end and it'll handle any exceptions within that promise chain. You can also pass an error handler to the done() method. The result could look something like this:
doSomethingAsync()
.then(function (result) { return somethingElseAsync(); })
.then(function (result) { return aThirdAsyncThing(); })
.done(
function (result) { doThisWhenAllDone(); },
function (err) { ohNoSomethingWentWrong(err); }
);
Finally, unhandled exceptions eventually end up at window.onerror, so you could capture them there. I would only do logging at this point; trying to recover your app and keep running from a top-level error handler is generally a bad idea.
I think you are asking for the equivalent to an ASP.NET Webforms Application_Error catchall. The WinJS equivalent to ASP.NET's Application_Error method is WinJS.Application.onerror.
The best way to use this, is in your default.js file (or similar) and add a listener like:
WinJS.Application.onerror = function(eventInfo){
var error = eventInfo.detail.error;
//Log error somewhere using error.message and error.description..
// Maybe even display a Windows.UI.Popups.MessageDialog or Flyout or for all unhandled exceptions
};
This will allow you to capture all unhandled exceptions that appear in the application gracefully.
Here's an alternative to all these solutions that I had to discover on my own. Whenever you use promise objects, make sure your done() call handles both success and error situations. If you don't handle failures, the system will end up throwing an exception that won't be caught either by try/catch blocks nor via the usual WinJS.Application.onerror methods.
This issue has cost me 2 Windows Store rejections already before I discovered it on my own... :(
link: How-To: Last Chance Exception Handling in WinJS Applications
All you really need to do to treat unhandled exceptions is hook into the WinJS Application.onerror event, like this (from a default.js file:)
(function () {
"use strict";
var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
var nav = WinJS.Navigation;
WinJS.strictProcessing();
app.onerror = function (error) {
//Log the last-chance exception (as a crash)
MK.logLastChanceException(error);
};
/* rest of the WinJS app event handlers and methods */
app.start();})();
Bear in mind that you have no access to network IO when the WinRT is crashing, but you can write to disk.