Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronous exception handling with bluebird promises

What is the best way to handle this scenario. I am in a controlled environment and I don't want to crash.

var Promise = require('bluebird');

function getPromise(){
    return new Promise(function(done, reject){
        setTimeout(function(){
                throw new Error("AJAJAJA");
        }, 500);
    });
}

var p = getPromise();
    p.then(function(){
        console.log("Yay");
    }).error(function(e){
        console.log("Rejected",e);
    }).catch(Error, function(e){
        console.log("Error",e);
    }).catch(function(e){
        console.log("Unknown", e);
    });

When throwing from within the setTimeout we will always get:

$ node bluebird.js

c:\blp\rplus\bbcode\scratchboard\bluebird.js:6
                throw new Error("AJAJAJA");
                      ^
Error: AJAJAJA
    at null._onTimeout (c:\blp\rplus\bbcode\scratchboard\bluebird.js:6:23)
    at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)

If the throw occurs before the setTimeout then bluebirds catch will pick it up:

var Promise = require('bluebird');

function getPromise(){

    return new Promise(function(done, reject){
        throw new Error("Oh no!");
        setTimeout(function(){
            console.log("hihihihi")
        }, 500);
    });
}

var p = getPromise();
    p.then(function(){
        console.log("Yay");
    }).error(function(e){
        console.log("Rejected",e);
    }).catch(Error, function(e){
        console.log("Error",e);
    }).catch(function(e){
        console.log("Unknown", e);
    });

Results in:

$ node bluebird.js
Error [Error: Oh no!]

Which is great - but how would one handle a rogue async callback of this nature in node or the browser.

like image 934
j03m Avatar asked Aug 05 '14 16:08

j03m


People also ask

Why do we use Bluebird promises?

The strongest feature of Bluebird is that it allows you to “promisify” other Node modules in order to use them asynchronously. Promisify is a concept applied to callback functions. This concept is used to ensure that every callback function which is called returns some value.

Can you return promise in a catch?

The Promise returned by catch() is rejected if onRejected throws an error or returns a Promise which is itself rejected; otherwise, it is fulfilled.

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.

What is promise try?

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.


2 Answers

Promises are not domains, they will not catch exceptions from asynchronous callbacks. You just can't do that.

Promises do however catch exceptions that are thrown from within a then / catch / Promise constructor callback. So use

function getPromise(){
    return new Promise(function(done, reject){
        setTimeout(done, 500);
    }).then(function() {
        console.log("hihihihi");
        throw new Error("Oh no!");
    });
}

(or just Promise.delay) to get the desired behaviour. Never throw in custom (non-promise) async callbacks, always reject the surrounding promise. Use try-catch if it really needs to be.

like image 169
Bergi Avatar answered Oct 08 '22 18:10

Bergi


After dealing with the same scenario and needs you are describing, i've discovered zone.js , an amazing javascript library , used in multiple frameworks (Angular is one of them), that allows us to handle those scenarios in a very elegant way.

A Zone is an execution context that persists across async tasks. You can think of it as thread-local storage for JavaScript VMs

Using your example code :

import 'zone.js'

function getPromise(){
  return new Promise(function(done, reject){
    setTimeout(function(){
      throw new Error("AJAJAJA");
    }, 500);
  });
}

Zone.current
  .fork({
    name: 'your-zone-name',
    onHandleError: function(parent, current, target, error) {
      // handle the error 
      console.log(error.message) // --> 'AJAJAJA'
      // and return false to prevent it to be re-thrown
      return false
    }
  })
  .runGuarded(async () => {
    await getPromise()
  })
like image 24
colxi Avatar answered Oct 08 '22 19:10

colxi