Usually, when unhandled rejection happens, Bluebird dispatches unhandledrejection event like this:
// The message is logged, great!
window.addEventListener("unhandledrejection", function(e) {
console.log("got unhandled rejection")
});
Promise.reject(new Error())
However, as soon as I explicitly mark a promise chain as .done(), the event is no longer dispatched:
// The message is not logged, boo!
window.addEventListener("unhandledrejection", function(e) {
console.log("got unhandled rejection")
});
Promise.reject(new Error()).done()
Please do not paste the boilerplate answer "bluebird is smart enough to handle .done() for you". I want to specifically ask about handling uncaught rejections when .done() is used. In particular I need this because unhandledrejection is fired whenever appropriate .catch is attached asynchronously, which it is in my case.
In Q it works as expected (although Q actually requires you to finish the promise chain with .done()):
Q.onerror = function() {
console.log("got unhandled rejection")
};
Q.reject(new Error()).done();
When you call done you're explicitly asking Bluebird to convert a rejection to a thrown exception.
You can catch all of those using window.onerror (or the addEventListener counterpart).
window.onerror = function(e){
// done makes it into a thrown exception - so catch it here.
};
P.S.
The reason people post the "bluebird is clever enough" boilerplate is mostly because it's right (and for what it's worth Q is too as of 1.3). Using .done everywhere is error prone and problematic for plenty of reasons. It's not supported in native promises either.
Related
I was writing code that does something that looks like:
function getStuffDone(param) { | function getStuffDone(param) {
var d = Q.defer(); /* or $q.defer */ | return new Promise(function(resolve, reject) {
// or = new $.Deferred() etc. | // using a promise constructor
myPromiseFn(param+1) | myPromiseFn(param+1)
.then(function(val) { /* or .done */ | .then(function(val) {
d.resolve(val); | resolve(val);
}).catch(function(err) { /* .fail */ | }).catch(function(err) {
d.reject(err); | reject(err);
}); | });
return d.promise; /* or promise() */ | });
} | }
Someone told me this is called the "deferred antipattern" or the "Promise constructor antipattern" respectively, what's bad about this code and why is this called an antipattern?
The deferred antipattern (now explicit-construction anti-pattern) coined by Esailija is a common anti-pattern people who are new to promises make, I've made it myself when I first used promises. The problem with the above code is that is fails to utilize the fact that promises chain.
Promises can chain with .then and you can return promises directly. Your code in getStuffDone can be rewritten as:
function getStuffDone(param){
return myPromiseFn(param+1); // much nicer, right?
}
Promises are all about making asynchronous code more readable and behave like synchronous code without hiding that fact. Promises represent an abstraction over a value of one time operation, they abstract the notion of a statement or expression in a programming language.
You should only use deferred objects when you are converting an API to promises and can't do it automatically, or when you're writing aggregation functions that are easier expressed this way.
Quoting Esailija:
This is the most common anti-pattern. It is easy to fall into this when you don't really understand promises and think of them as glorified event emitters or callback utility. Let's recap: promises are about making asynchronous code retain most of the lost properties of synchronous code such as flat indentation and one exception channel.
What's wrong with it?
But the pattern works!
Lucky you. Unfortunately, it probably doesn't, as you likely forgot some edge case. In more than half of the occurrences I've seen, the author has forgotten to take care of the error handler:
return new Promise(function(resolve) {
getOtherPromise().then(function(result) {
resolve(result.property.example);
});
})
If the other promise is rejected, this will happen unnoticed instead of being propagated to the new promise (where it would get handled) - and the new promise stays forever pending, which can induce leaks.
The same thing happens in the case that your callback code causes an error - e.g. when result doesn't have a property and an exception is thrown. That would go unhandled and leave the new promise unresolved.
In contrast, using .then() does automatically take care of both these scenarios, and rejects the new promise when an error happens:
return getOtherPromise().then(function(result) {
return result.property.example;
})
The deferred antipattern is not only cumbersome, but also error-prone. Using .then() for chaining is much safer.
But I've handled everything!
Really? Good. However, this will be pretty detailed and copious, especially if you use a promise library that supports other features like cancellation or message passing. Or maybe it will in the future, or you want to swap your library against a better one? You won't want to rewrite your code for that.
The libraries' methods (then) do not only natively support all the features, they also might have certain optimisations in place. Using them will likely make your code faster, or at least allow to be optimised by future revisions of the library.
How do I avoid it?
So whenever you find yourself manually creating a Promise or Deferred and already existing promises are involved, check the library API first. The Deferred antipattern is often applied by people who see promises [only] as an observer pattern - but promises are more than callbacks: they are supposed to be composable. Every decent library has lots of easy-to-use functions for the composition of promises in every thinkable manner, taking care of all the low-level stuff you don't want to deal with.
If you have found a need to compose some promises in a new way that is not supported by an existing helper function, writing your own function with unavoidable Deferreds should be your last option. Consider switching to a more featureful library, and/or file a bug against your current library. Its maintainer should be able to derive the composition from existing functions, implement a new helper function for you and/or help to identify the edge cases that need to be handled.
Now 7 years later there is a simpler answer to this question:
How do I avoid the explicit constructor antipattern?
Use async functions, then await every Promise!
Instead of manually constructing nested Promise chains such as this one:
function promised() {
return new Promise(function(resolve) {
getOtherPromise().then(function(result) {
getAnotherPromise(result).then(function(result2) {
resolve(result2);
});
});
});
}
just turn your function async and use the await keyword to stop execution of the function until the Promise resolves:
async function promised() {
const result = await getOtherPromise();
const result2 = await getAnotherPromise(result);
return result2;
}
This has various benefits:
Calling the async function always returns a Promise, which resolves with the returned value and rejects if an error get's thrown inside the async function
If an awaited Promise rejects, the error get's thrown inside the async function, so you can just try { ... } catch(error) { ... } it like the synchronous errors.
You can await inside loops and if branches, making most of the Promise chain logic trivial
Although async functions behave mostly like chains of Promises, they are way easier to read (and easier to reason about)
How can I await a callback?
If the callback only calls back once, and the API you are calling does not provide a Promise already (most of them do!) this is the only reason to use a Promise constructor:
// Create a wrapper around the "old" function taking a callback, passing the 'resolve' function as callback
const delay = time => new Promise((resolve, reject) =>
setTimeout(resolve, time)
);
await delay(1000);
If await stops execution, does calling an async function return the result directly?
No. If you call an async function, a Promise gets always returned. You can then await that Promise too inside an async function. You cannot wait for the result inside of a synchronous function (you would have to call .then and attach a callback).
Conceptually, synchronous functions always run to completion in one job, while async functions run synchronously till they reach an await, then they continue in another job.
I'm new to promises, but as I understand it, .catch usually belongs at the end of a chain of promises:
promiseFunc()
.then( ... )
.then( ... )
.catch( // catch any errors along the chain )
What if the promises are split in between functions? Do I catch at the end of every function?
func1 () {
promiseFunc1()
.then((result) => {
promiseFunc2()
)
// should I .catch here?
}
func2 () {
func1()
.then((result) => {
// do stuff
})
.catch(console.log.bind(console)); // this also catches errors from func1
}
Maybe this is a symptom of another error (in which I'd love to hear if I'm doing this wrong), but when I try catching at the end of func1, I end up reaching the .then block of func2, with result = undefined. After deleting the catch in func1, it works -- but it feels wrong that func1 should expect any functions calling it to catch its errors.
.catch() works very much like try/catch. You should .catch() wherever you want or need to handle the error and either log something or change the course of the chain.
If all you want is for the promise chain to abort when an error occurs, then you can just put one .catch() at the very end of the chain and deal with the error there.
If, on the other hand, you have some sub-part of the chain that, if it has a rejection you want to do something different and allow the chain to continue or to take a different path, then you need to .catch() at that level where you want to influence things if there's an error.
All or Nothing Catch at the End
So, let's say you have four functions that all return promises.
If you do this:
a().then(b).then(c).then(d).then(finalResult => {
// final result here
}).catch(err => {
// deal with error here
});
Then, if anyone of your four promises rejects, then the rest of the chain will abort and it will skip to your one .catch(). For some operations, this is the desired behavior. If you intend to fetch some data from an external server, use that data to then fetch some other data from another external server and then write that data to disk, the whole process is pretty much all or nothing. If any of the earlier steps fails, there's nothing else you can do as the whole operation has failed, you may as well just use one .catch() at the end and report that error.
Intervening Catch to Change the Behavior Mid-Chain upon Error
On, the other hand suppose you have a situation were you want to fetch some data from an external server, but if that server is down, then you want to fetch the data from an alternate server. In that case, you want to catch the error from the first fetch and try something else. So, you'd use a .catch() on the very first step and attempt something different:
fetch1().catch(fetch2).then(b).then(c).then(finalResult => {
// final result here
}).catch(err => {
// deal with error here
});
Logging and Rethrow
When building sub-systems that others will use, it is often useful to log certain types of errors. So, even the whole promise chain might be all or nothing, you still may want to log an error earlier in the chain and then rethrow the error so that the chain continues in the rejected state:
function someFuncOthersUse() {
return a().then(b).then(c).catch(err => {
// you want your own logging any time a or b or c rejects
console.log("Error on fetchB", err);
throw err;
});
}
The caller will then be doing:
someFuncOthersUse().then(finalResult => {
// final result here
}).catch(err => {
// deal with error here
});
I'm not sure if I'm understanding a certain aspect of promises correctly and I couldn't find what I was looking for after a brief google/SO search.
Can a resolved promise returned to a rejected callback ever fire a resolved method later in the chain? Using jQuery deferreds (and it's deprecated pipe method) as an example:
x = $.Deferred();
x
.then(null,function() {
return $.Deferred().resolve();
})
.then(function() {
console.log('resolved');
},function() {
console.log('rejected');
});
The above code, logs rejected, I would've expected it to log resolved because the Deferred in the first error callback returns a resolved promise.
On the contrary, the same code using jQuery's deprecated pipe method logs, as I would've expected resolved.
x = $.Deferred();
x
.pipe(null,function() {
return $.Deferred().resolve();
})
.pipe(function() {
console.log('resolved');
},function() {
console.log('rejected');
});
Am I thinking about something wrong by trying to resolve a promise inside a rejected callback?
For anybody who has run into this same thought process, the following page helped me out: https://gist.github.com/domenic/3889970
Specifically:
...you can feed the return value of one function straight into another,
and keep doing this indefinitely. More importantly, if at any point
that process fails, one function in the composition chain can throw an
exception, which then bypasses all further compositional layers until
it comes into the hands of someone who can handle it with a catch.
It seems jQuery's pipe method was a violation of the Promise's specification.
I've been looking at bluebird promises and how promise.try differs from a promise.resolve.then when an error is thrown. Firstly some code using promise.try where it throws a synchronous error
Promise.try(function() {
throw new Error('error');
}).catch(function(e) {
console.log(e);
});
secondly some code which throws a synchronous error on resolve
Promise.resolve().then(function() {
throw new Error('error');
}).catch(function(e) {
console.log(e);
});
As far as I'm aware they both behave the same. Is promise.try essentially a cleaner way of resolving the promise?
The docs says promise.try:
will catch all errors in their Promise .catch handlers instead of having to handle both synchronous and asynchronous exception flows.
In the case of the example given in the docs:
function getUserById(id) {
return Promise.try(function() {
if (typeof id !== "number") {
throw new Error("id must be a number");
}
return db.getUserById(id);
});
}
if the synchronous error is thrown the asynchronous code will never be reached. Would there be any difference if you put the code above in a promise.resolve().then(..)?
Any clarification/examples of promise.try will be much appreciated.
Adding to Bergi's answer: Promise.try is for those times you can't use Promise.method. The goal is to avoid cases where you have sync exceptions mixed with rejections.
Generally, whenever you're considering using Promise.try give Promise.method a spin.
var fn = Promise.method(function(){
// can throw or return here, and it'll behave correctly
});
Is roughly the same as:
var fn = function(){
return Promise.try(function(){
// can throw or return here, and it'll behave correctly
});
});
As far as I'm aware they both behave the same.
Yes, mostly. However, the .then(…) callback will be invoked asynchronously, while Promise.try is synchronously executing your function.
Is promise.try essentially a cleaner way of resolving the promise?
Yes, it does provide a cleaner (less confusing) notation. But it's more of an optimisation, because it doesn't create any Promise.resolve(undefined) in the first place.
Using CasperJS how do I catch and handle CasperError?
The default appears to continue execution of the program (which does nothing but propagates the error).
These errors are logged to the console/stdout but I don't seem to see a way (from the docs) to catch and handle these errors.
Example:
this.fillSelectors(selector, data);
May produce:
CasperError: Errors encountered while filling form: form not found
I know I can check to make sure everything exists before calling, but is there a way to catch after the fact? (this applies to many other operations like casper.click as well)
I use something currently like this:
casper.on('error', function(msg,backtrace) {
this.capture('./out/error.png');
throw new ErrorFunc("fatal","error","filename",backtrace,msg);
});
and then I have a custom function ErrorFunc to process array of any warnings or a fatal error.
If you have an unsuccessful click it should throw the casper.on('error'). So you can put custom code there for how you would like to handle the error.
Here's the documentation for Casper events.
var casper = require('casper').create({
onError: function(msg, backtrace) {
this.capture('error.png');
throw new ErrorFunc("fatal","error","filename",backtrace,msg);
}
});
This works pretty well.
(See http://docs.casperjs.org/en/latest/modules/casper.html#index-1)
This is the complete solution for who need it ^^
casper.on('error', function(msg, backtrace) {
this.capture('/tmp/error.png');
console.log('backtrace: ' + JSON.stringify(backtrace, null, 4));
console.log('message: ' + JSON.stringify(msg, null, 4));
console.log('check capture in /tmp/error.png');
casper.exit(1);
})