Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bluebird.JS Promise: new Promise(function (resolve, reject){}) vs Promise.try(function(){})

When should I use which? Are the following the same?

new Promise() example:

function multiRejectExample(){ 
  return new Promise(function (resolve, reject){
    if(statement){
      console.log('statement 1');
      reject(throw new Error('error'));
    }
    if(statement){
     console.log('statement 2');
     reject(throw new Error('error')); 
   }
  });
}

Promise.try() example:

function tryExample(){
  return Promise.try(function(){
    if(statement){
      console.log('statement 1');
      throw new Error('error');
    }
    if(statement){
     console.log('statement 2');
     throw new Error('error'); 
   }
  });
}
like image 269
esanz91 Avatar asked Aug 21 '15 19:08

esanz91


3 Answers

You can use mostly either in this case (with one behavior difference). The first is standard promise functionality and will work with any promise library.

Promise.try() is a feature specifically implemented by the Bluebird library and is not part of any standards process that I am aware of.

The reason to use Promise.try() is if you have a function that returns a promise, but the code that generates that promise might also cause a synchronous exception. Since that exception is not inside of any promise handler, you'd have a mix of error handling. Some code execution paths might cause a promise to be returned that would resolve or reject and other code execution paths might throw an exception. To code this safely, you'd have to both respond to the promise and put a try/catch block around the code which gets unwieldy.

Promise.try() is simply a means of automatically catching any exceptions and turning them into a rejection (similar to what happens in .then() handlers).

In your two cases, Promise.try() does not benefit you in that way because the new Promise() callback already catches exceptions and turns them into rejections so that functionality is already being done for you there. You can see that demonstrated here: http://jsfiddle.net/jfriend00/wLov9844/

The Bluebird doc offers this example which shows the benefit more clearly:

function getUserById(id) {
    return Promise.try(function() {
        if (typeof id !== "number") {
            // Courtesy of Promise.try() this exception will be turned 
            // into a returned promise that is rejected with the 
            // exception as the reason
            throw new Error("id must be a number");
        }
        return db.getUserById(id);
    });
}

getUserById().then(successFn, errFn);

The use of Promise.try() here makes sure that getUserById() will always return a promise, even if the code inside of that method throws an exception synchronously. This simplifies use of getUserById() since you can always just respond to the promise and don't have to use your own exception handler around it.

Without Promise.try(), you could code the same thing yourself like this (to catch all possible synchronous exceptions inside the function):

function getUserById(id) {
    try {
        if (typeof id !== "number") {
            throw new Error("id must be a number");
        }
        return db.getUserById(id);
    } catch(e) {
        return Promise.reject(e);
    }
}

getUserById().then(successFn, errFn);

Or, you could code it like this:

function getUserById(id) {
    if (typeof id !== "number") {
        throw new Error("id must be a number");
    }
    return db.getUserById(id);
}

try {
    getUserById().then(successFn, errFn);
} catch(e) {
    errFn(e);
}

Presumably, you can see how Promise.try() can simplify things in some circumstances.


FYI, in your first example, you are using invalid syntax. You can do this:

reject(throw new Error('error')); 

I'm assuming what you meant was this:

reject(new Error('error')); 

Though I don't think this is really what you were asking about, Promise.try() will also automatically return a resolved promise if you don't return a promise yourself. Since one path through your first example doesn't resolve or reject, this will cause a difference in your two examples.

like image 76
jfriend00 Avatar answered Oct 22 '22 02:10

jfriend00


They aren't the same.

Consider the situation where both statements are false. In that case, multiRejectExample() will never reject or resolve the returned promise, whereas tryExample() will "fall through" and resolve the promise automatically (with a value of undefined because you don't return anything).

To demonstrate:

var Promise = require('bluebird');

function test1() {
  return Promise.try(function() { });
}

function test2() {
  return new Promise(function(resolve, reject) { });
}

test1().then(function() { console.log('resolved #1'); });
test2().then(function() { console.log('resolved #2'); });

This will log resolved #1 but not resolved #2 because the promise is never actually resolved (nor rejected).

like image 22
robertklep Avatar answered Oct 22 '22 01:10

robertklep


Are the following the same?

No. As @robertklep already mentioned, they do have different results when statement is false. Promise.try catches exceptions and otherwise resolves with the return value from the function, while the Promise constructor just creates a new promise and doesn't care when it is never resolve()d.

When should I use which?

You should use the Promise constructor iff, and really only if, you are promisifying an asynchronous callback API. Everything else, especially when involving other promises, is basically an antipattern.
So don't use it like you have in your question.

You usually don't throw either. You're writing a function that returns a promise (because it is asynchronous), and it should always return a promise. If you want to return a promise that is rejected with an error, you can create one explicitly by using Promise.reject. The proper approach would therefore be

function multiRejectExample(){ 
    if (statement){
        console.log('statement 1');
        return Promise.reject(new Error('error'));
    }
    if (statement){
        console.log('statement 2');
        return Promise.reject(new Error('error')); 
    }
    return Promise.resolve();
}

Of course, this particular example doesn't make much sense, because none of your cases is asynchronous and there's no reason to use promises at all. Just use a synchronous function that throws exceptions. Typically, you'd have some return … in the end that is an actually asynchronous promise, not one that is fulfilled with undefined.

Now, if you have such a function, and are tired of repeatedly writing return Promise.reject, you can use the Bluebird-specific Promise.try and Promise.method methods as syntactic sugar.

like image 29
Bergi Avatar answered Oct 22 '22 02:10

Bergi