Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Bluebird to promisify the exported functions on a constructor-built "class"

I've got a service, PageService, that I test like this (simplified)...

var database = require("../database/database");
var PageService = require("./pageService");

describe("PageService", function () {
    var pageService = {};

    before(function (done) {
        pageService = new PageService(database);
    }

    it("can get all Pages", function (done) {
        pageService.getAll(function (err, pages) {
            if (err) return done(err);

            pages.should.be.instanceOf(Array);
            pages.length.should.be.greaterThan(1);
            done();
    });
});

I've struggled to use bluebird to promisify all the methods hanging off of PageService (getAll, getById, create, update, delete, etc). I've looked at several discussions on the topic, but most seem to concern trying to get constructors to return a promise. I just want to promisify all the functions hanging off the class I create via a constructor. It's the pageService = new PageService(database); that I can't get past for promisification.

PageService is just using a basic constructor pattern - e.g.

self.getAll = function(next) {
    self.collection.find({}, function(err, docs) {
        if (err) return next(err);

        next(null, docs);
    });
};

If someone could show me the proper way to easily promisify all the functions hanging off the object returned from the constructor, I'd appreciate it. I'm also open to the fact that I may be doing this the wrong way. I'm pretty new to promises in general and welcome guidance.

Update

I got the functions promisified by doing the following...

pageService = new PageService(database);
Promise.promisifyAll(pageService);

... but that seems like a bad practice to run through promisification every time I new up an instance of the service. I'd like a way to promisify once and only once. I recognize that manually returning promises in the service might be the solution, but was hoping for something more elegant via bluebird magic.

like image 467
Tim Hardy Avatar asked Jul 25 '15 05:07

Tim Hardy


2 Answers

PageService module:

function PageService(collection) {
    this.collection = collection;
}
PageService.prototype.getAll = function(next) {
    this.collection.find({}, function(err, docs) {
        if (err) return next(err);
        next(null, docs);
    });
};

module.exports = PageService;

test module

var should = require("should");
var Promise = require("bluebird");
var database = { // mockup
    find: function (options, next) {
        next(null, ['the', 'list', 'of', 'docs']);
    }
};
var PageService = require("./PageService");

Promise.promisifyAll(PageService.prototype);

describe("PageService", function () {
    var pageService;

    before(function () {
        pageService = new PageService(database);
    });

    it("can get all pages", function () {
        return pageService.getAllAsync()
        .then(function (pages) {
            pages.should.be.instanceOf(Array);
            pages.length.should.be.greaterThan(1);
        });
    });
});
like image 70
Tomalak Avatar answered Nov 12 '22 09:11

Tomalak


You shouldn't be concerned about promisification every time. All it does is just wrapping your callbacks in appropriate promise code. There is no performance penalty to be concerned about and it's a perfectly acceptable solution.

Also there is no other way to do it. It all comes down to two solutions: using Bluebird promisification or manually rewriting your service to work with promises.

like image 32
krl Avatar answered Nov 12 '22 10:11

krl