cujojs / when

A solid, fast Promises/A+ and when() implementation, plus other async goodies.
Other
3.44k stars 396 forks source link

How to write a function to support promises and nodeback #408

Closed jamlen closed 9 years ago

jamlen commented 9 years ago

I'm adding some functions to an API which I want to support both promises and nodeback style usage, but I don't want to wrap the nodeback-style function with another function (what I understand lift would do) rather to do something similar to Q's nodeify method.

An example of exactly what I want is here: http://www.mono-software.com/blog/post/Mono/243/Creating-NodeJS-modules-with-both-promise-and-callback-API-support-using-Q/ and notice that the nodeify is done inside the method not from the outside.

How can I do this with when?

rkaw92 commented 9 years ago

Hi, it's important to recognize that what really happens under the hood, using the common Promises/A+ API, is this:

if(callback){
  promiseToReturn.done(function success(value){
    callback(null, value);
  }, function failure(reason){
    callback(reason, null);
  });
}

And, in fact, this is a pattern that we use when bridging to legacy code. Now, when.js additionally provides https://github.com/cujojs/when/blob/master/docs/api.md#nodebindcallback , which lets you replace the manual .done call with a one-liner:

if(callback){
  nodefn.bindCallback(promiseToReturn, callback);
}

Of course, you still have to return the promise from the function in case the caller does not wish to use callbacks. Hope that helps!

briancavalier commented 9 years ago

@jamlen Yep, what @rkaw92 said :) Have a look at when/node's bindCallback. It returns the promise you pass to it, and also checks whether callback is provided or not. That makes it easy to write a one-liner. Here's a quick refactor of the example in your link.

// dual-module.js
var when = require('when');
var bindCallback = require('when/node').bindCallback;

module.exports = {
    getFullName: function (firstName, lastName, callback) {
        var deferred = when.defer();

        if (firstName && lastName) {
            var fullName = firstName + " " + lastName;
            deferred.resolve(fullName);
        }
        else {
            deferred.reject("First and last name must be passed.");
        }

        return bindCallback(deferred.promise, callback);
    }
};
jamlen commented 9 years ago

Excellent thanks guys, would it be worth adding this to some part of the docs?

briancavalier commented 9 years ago

@jamlen Yep, I think it would be great to provide a better example than what's there now. Would you be up for sending a PR?

jamlen commented 9 years ago

Sure thing!

briancavalier commented 9 years ago

Cool, thanks :)

jamlen commented 9 years ago

So given the docs say that the use of when.deferred is discouraged, I assume that this is an appropriate refactor using when.promise?

var when = require('when');
var bindCallback = require('when/node').bindCallback;

module.exports = {
    getFullName: function (firstName, lastName, callback) {
        return bindCallback(when.promise(function(resolve, reject) {
            if (firstName && lastName) {
                var fullName = firstName + " " + lastName;
                resolve(fullName);
            }
            else {
                reject("First and last name must be passed.");
            }
        }, callback);
    }
};
briancavalier commented 9 years ago

Yep, exactly.

briancavalier commented 9 years ago

Closed by #409