Оборачиваем обратные вызовы Node.js в промисы с помощью Bluebird

Как обернуть обратный вызов Node.js с помощью Promise в Bluebird? Это то, что я придумал, но хотел знать, есть ли лучший способ:

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

Есть ли более чистый способ сделать это, если нужно вернуть только ошибку?

Изменить Я попытался использовать Promise.promisifyAll(), но результат не распространяется на предложение then. Мой конкретный пример показан ниже. Я использую две библиотеки: а) sequenceize, который возвращает промисы, б) супертест (используется для тестирования http-запросов), который использует обратные вызовы в стиле узла. Вот код без использования promisifyAll. Он вызывает sequenceize для инициализации базы данных, а затем делает HTTP-запрос для создания заказа. Оба оператора console.log печатаются правильно:

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();
                });
        });
    });

    ...
});

Теперь я пытаюсь использовать promisifyAll, чтобы связать вызовы с 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();
        });
    });

    ...
});

Когда я добираюсь до второго console.log, аргумент res не определен.

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

Что я делаю не так?


person Naresh    schedule 31.03.2014    source источник
comment
возможный дубликат Как преобразовать существующий API обратного вызова в обещания?   -  person Bergi    schedule 31.03.2014
comment
Смотрите мой отредактированный ответ (комментарий для уведомления)   -  person Esailija    schedule 31.03.2014
comment
Вы привязали .then к .then, который ничего не возвращает. Ваш исходный вопрос является дубликатом, и ваше редактирование касается правильного использования обработчиков .then.   -  person Benjamin Gruenbaum    schedule 31.03.2014


Ответы (1)


Вы не вызываете версию, возвращающую обещание, и не возвращаете ее.

Попробуй это:

   // 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(); 
person Esailija    schedule 31.03.2014
comment
Потрясающий! Это решило мою проблему. Был небольшой глюк. Вызов request(app) фактически возвращает тестовый объект (см. здесь: github .com/visionmedia/supertest/blob/master/index.js#L19-34). Поэтому мне пришлось изменить вызов обещания на Promise.promisifyAll(request.Test.prototype). К счастью, request.Test был разоблачен. Что я должен был сделать, если бы это было не так? Кроме того, что, если все методы объекта не предназначены для обратного вызова или обратный вызов не является обязательным? Безопасно ли использование promisifyAll() в таких случаях? Спасибо за вашу помощь. - person Naresh; 31.03.2014
comment
@Naresh, если класс не будет выставлен, вы можете сделать Promise.promisifyAll(request().constructor.prototype) (вне любого кода, это нужно сделать только один раз за все время существования приложения) - person Esailija; 31.03.2014
comment
@Naresh Неважно, какие существуют функции, если вы их не вызываете. Если вы вызываете someMethodAsync(), а someMethod не является функцией обратного вызова, это просто вызовет странные ошибки, но зачем вам вызывать someMethodAsync(), зная, что someMethod не является функцией обратного вызова? :П - person Esailija; 31.03.2014
comment
Превосходно! Вы ответили на все мои мучительные вопросы. На самом деле, перечитывая документы, все ответы есть, но они не бросаются мне в глаза. Возможно, поможет простой конкретный пример node.js на домашней странице Bluebird. Пример, начинающийся с «Вы также можете использовать обещания для улучшения кода, написанного с помощью вспомогательных функций обратного вызова», для меня слишком сложен — я не связывал его с обратным вызовом Node.js, и поэтому я придумал первое решение, указанное в моем вопросе. (тот, который создает новый Promise вручную). Еще раз спасибо за вашу помощь. - person Naresh; 31.03.2014
comment
@Naresh Спасибо за отзыв, я что-нибудь придумаю - person Esailija; 31.03.2014