Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I work with the Bluebird error handler?

Introduction

This question aims to, eventually, resolve a problem I'm having in development with Bluebird. However, I'm also using the opportunity to get some things clarified, so there will be side-questions. I also apologize in advance for any feelings of confusion or boredom you might experience while reading the story to come.


Questions

As far as my understanding goes, Bluebird attempts to intelligently catch ignored rejections, according to the following strategy:

The second approach, which is what bluebird by default takes, is to call a registered handler if a rejection is unhandled by the start of a second turn. -- Bluebird Readme # Error Handling

Now herein lies the first side-question: What does "the start of a second turn" mean?

Later in the same section, the following is documented:

Of course this is not perfect, if your code for some reason needs to swoop in and attach error handler to some promise after the promise has been hanging around a while then you will see annoying messages. In that case you can use the .done() method to signal that any hanging exceptions should be thrown. -- Bluebird Readme # Error Handling

Now, I believe that I ran in to the situation described above with my use case being as follows:

  • I call a function which will provide me the promise to which I attach a .catch():

    lib.loadUrls()
    .catch(function(e){console.log(e);});
    
  • Internally, that function loads content from URL1 and based on the content, loads content from URL2 in sequence:

    lib.loadUrls =
      return this.loadUrl1()
      .then(this.loadUrl2.bind(this))
    
  • If the second promise in this chain is rejected the error is handled by the catch first, and then by Bluebirds Possibly unhandled error handler as well.

This last behavior is unwanted and I can not figure out why it's doing this. So question two could be: Why, despite an error handler being attached and executed, does Bluebird still consider the possibility of the error being "unhandled"?

I'm thinking, apparently the promise "has been hanging around for a while" by the time the rejection propagates to the .catch(). In which case I should solve it (according to the quoted documentation) by "using the .done()".

Now, I've tried several things, but I can't quite figure out how to "use the .done" in this scenario. (It doesn't help that .done() returns undefined, preventing me from .finally-ing.)

So this introduces my third, and fourth questions: How do I use .done() in this scenario, and how do I explicitly conclude a promise-chain, but still attach a .finally()

EDIT 1: I've created some JSFiddles to reproduce the bug:

  • Using Bluebird 1.0.5 reproduces the bug.
  • Using the latest Bluebird.js reproduces the bug (at this time)
  • Using Beta version 0.10.0-1 does not reproduce the bug.

EDIT 2: The dev fixed the bug.

like image 539
Avaq Avatar asked Feb 18 '14 06:02

Avaq


People also ask

How do you use a promise map?

map. Given a finite Iterable (arrays are Iterable ), or a promise of an Iterable , which produces promises (or a mix of promises and values), iterate over all the values in the Iterable into an array and map the array to another using the given mapper function.


2 Answers

This was indeed just a regression bug in bluebird and is now fixed.

The bit about needing to use .done() is pretty much theoretical, you won't run in a situation in practice where you would need to attach error handlers in such a way that would cause false positives to be reported.

like image 62
Esailija Avatar answered Sep 23 '22 13:09

Esailija


It's most likely Bluebird bug, as handled error should not be reported (assuming you properly handle promises in loadUrls body). So probably you should report it to Bluebird issue tracker.

Concerning done, it's pure access function that's best if used instead of then or catch when you just process resolved value.

It's good to treat done as first choice function and use then and catch only if you really need transformation to other promise, with such approach you also don't need to rely on buggy errors monitoring (best to turn it off completely).

In your case done should be used as:

lib.loadUrls().done(); // eventual error will be thrown

and if for some reason you want to handle error specifically (e.g. in running server you don't want it throw) do:

lib.loadUrls().done(null, function (error) {
  // handle error
});

EDIT:

Just noticed, that you still want to process promise returned by lib.loadUrls().catch(..) with finally. In such case done is not a solution. done should only be used as final call, but you can combine it with finally as follows:

lib.loadUrls().finally(function () {
  // cleanup
}).done();
like image 43
Mariusz Nowak Avatar answered Sep 23 '22 13:09

Mariusz Nowak