Search code examples
javascriptnode.jspromisebluebird

Should simple statement be surrounded by Promise.try in bluebird promises?


Using promisified request module, i want to get the body attribute of the response. Not sure if I should use a Promise try.

So which one is correct here ?

A. Without Promise.try

request.getAsync("http://example.com")
       .then( x => x.body )
       .tap( x=> console.log('body:',x) )

B. With Promise.try

request.getAsync("http://example.com")
       .then( x => Promise.try( () => x.body ) )
       .tap( x=> console.log('body:',x) )

Solution

  • After the first element of a promise chain, an error is guaranteed to throw down the chain's error path and become available to be caught by a .then(null, errorHandler) or .catch(errorHandler).

    Therefore there is no point using Bluebird's Promise.try() other than to get a chain reliably started. Doing so will just bulk up your code and make it less efficient.

    How about inner chains?

    This is more interesting. Inner chains are commonly used to allow access to earlier results via closure. Flattened chains lose this ability.

    Consider :

    request.getAsync("http://example.com")
    .then( x => {
        return untrusted.method().then((y) => other.method(x, y));
    });
    

    The inner chain has its own starter, untrusted.method(), which may cause failure in one of two ways:

    • it may throw.
    • it may return a non-thenable.

    A throw is unavoidable (without addressing untrusted.method()), though the thrown error will be catchable in the outer chain.

    But a non-thenable can be guarded against. You can write :

    request.getAsync("http://example.com")
    .then(x => {
        return Promise.try(untrusted.method).then((y) => other.method(x, y) );
    });
    

    Now Promise.try() ensures the inner chain has a reliable start. Promise.try(untrusted.method) is guaranteed to be thenable.

    As a bonus, an error thrown by untrusted.method() is now catchable in the outer chain or the inner chain, which can be useful, particularly for error recovery.

    Promise.resolve(untrusted.method()) would similarly protect against a non-thenable being returned but would not allow a synchronous throw to be caught within the inner chain.

    So in summary,

    • within a promise chain, there's no value in wrapping synchronous expression(s) in Promise.try().
    • within a promise chain , there's no value in wrapping trusted, asynchronous function/method calls in Promise.try().
    • within a promise chain, there is potentially great value in wrapping an untrusted function/method call in Promise.try() to guarantee an inner chain's reliable start.