8

How do I wrap a Node.js callback using a Promise in Bluebird? This is what I came up with, but wanted to know if there is a better way:

return new Promise(function(onFulfilled, onRejected) {
    nodeCall(function(err, res) {
            if (err) {
                onRejected(err);
            }
            onFulfilled(res);
        });
});

Is there a cleaner way to do this if only an error needs to be returned back?

Edit I tried to use Promise.promisifyAll(), but the result is not being propagated to the then clause. My specific example is shown below. I am using two libraries: a) sequelize, which returns promises, b) supertest (used for testing http requests), which uses node style callbacks. Here's the code without using promisifyAll. It calls sequelize to initialize the database and then makes an HTTP request to create the order. Bosth console.log statements are printed correctly:

var request = require('supertest');

describe('Test', function() {
    before(function(done) {
        // Sync the database
        sequelize.sync(
        ).then(function() {
            console.log('Create an order');
            request(app)
                .post('/orders')
                .send({
                    customer: 'John Smith'
                })
                .end(function(err, res) {
                    console.log('order:', res.body);
                    done();
                });
        });
    });

    ...
});

Now I try to use promisifyAll, so that I can chain the calls with then:

var request = require('supertest');
Promise.promisifyAll(request.prototype);

describe('Test', function() {
    before(function(done) {
        // Sync the database
        sequelize.sync(
        ).then(function() {
            console.log('Create an order');
            request(app)
                .post('/orders')
                .send({
                    customer: 'John Smith'
                })
                .end();
        }).then(function(res) {
            console.log('order:', res.body);
            done();
        });
    });

    ...
});

When I get to the second console.log the res argument is undefined.

Create an order
Possibly unhandled TypeError: Cannot read property 'body' of undefined

What am I doing wrong?

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
Naresh
  • 23,937
  • 33
  • 132
  • 204
  • 1
    possible duplicate of [How do I convert an existing callback API to promises?](http://stackoverflow.com/questions/22519784/how-do-i-convert-an-existing-callback-api-to-promises) – Bergi Mar 31 '14 at 13:04
  • See my edited answer (commenting for notification) – Esailija Mar 31 '14 at 14:46
  • You have `.then` chained to a `.then` that returns nothing. Your original question is a duplicate and your edit is just about using `.then` handlers correctly. – Benjamin Gruenbaum Mar 31 '14 at 15:36

1 Answers1

8

You are not calling the promise returning version and not returning it either.

Try this:

   // Add a return statement so the promise is chained
   return request(app)
            .post('/orders')
            .send({
                customer: 'John Smith'
            })
            // Call the promise returning version of .end()
            .endAsync(); 
Esailija
  • 138,174
  • 23
  • 272
  • 326
  • Awesome! This solved my issue. There was a minor glitch. The call to `request(app)` actually returns a Test object (see here: https://github.com/visionmedia/supertest/blob/master/index.js#L19-34). So I had to change the promisify call to `Promise.promisifyAll(request.Test.prototype)`. Fortunately, request.Test was exposed. What should I have done if it was not? Also what if all the methods of the object are not intended to take a callback, or the callback is optional? Is the use of promisifyAll() safe in those cases? Thanks for your help. – Naresh Mar 31 '14 at 15:51
  • @Naresh if the class wouldn't be exposed, you can do `Promise.promisifyAll(request().constructor.prototype)` (outside any code, it only needs to be done once per whole lifetime of application) – Esailija Mar 31 '14 at 15:53
  • @Naresh It doesn't matter what kind of functions there are if you are not calling them. If you call `someMethodAsync()` and someMethod is not a callback function it will just cause weird bugs - but why would you call `someMethodAsync()` knowing that someMethod is not a callback taking function? :P – Esailija Mar 31 '14 at 15:55
  • Excellent! You have answered all my lingering questions. Actually rereading the docs, all the answers are there but they did not jump out at me. Perhaps a simple node.js specific example on the Bluebird home page would help. The example that starts with "You can also use promises to improve code that was written with callback helpers" is a little too complex for me - I did not connect it with Node.js callback and hence I came up with the first solution listed in my question (the one that creates a new Promise manually). Again, thanks for your help. – Naresh Mar 31 '14 at 16:09
  • @Naresh Thanks for the feedback, I will come up with something – Esailija Mar 31 '14 at 16:16