Q обещание .then() не определено

Я пытаюсь асинхронно извлечь данные из базы данных MySQL, а затем обработать результаты с обещанием Q (первая попытка). Я использую функцию Q defer(), указанную здесь. Я создал объект-оболочку, который я в конечном итоге прикреплю к req.db или получу доступ через круговую зависимость от com.db (как он используется в настоящее время), чтобы разрешить использование API в моем приложении.

Node выдает ошибку, указывающую на обратный вызов .then:

this.go().then(function(stuff){
                          ^
TypeError: Cannot call method 'then' of undefined

Я пробовал следующее:

var com = require('./mainRebuild');

function Base() {
    this.results = [];
    this.query = 'query here';
    this.go = function() {

        com.pool.getConnection(function(err, con) {

            if (err) throw err;
            /*
            THIS WORKS FINE (with callbacks)
             con.query('SELECT * FROM Users', function(err, rows){
                if (err) throw err;
                if (rows){

                  for (var i = 0; i < rows.length; i++){
                    this.results.push(rows[i]);
                    //console.log(rows[i]);
                  }
                }
                con.release();
                return this.results;
              }.bind(this));

              */
            var defer = com.Q.defer();
            con.query('SELECT * FROM Users', defer.makeNodeResolver());
            return defer.promise();
        }.bind(this));
    }
}

function DB() {
    this.getAllUsers = function() {
        this.query = 'SELECT * FROM Users';
        this.go().then(function(stuff) {
            console.log(stuff);
        }, function(err) {
            console.log(err);
        });
    }

}

DB.prototype = new Base();

module.exports = DB;

Объект com существует в основном приложении, он содержит все необходимые модули.

Я также пробовал (кажется гораздо более неправильным):

this.deffered = com.Q.defer();
com.pool.getConnection(function(err, con) {

            if (err) this.deferred.reject(new Error(err));

            con.query('SELECT * FROM Users', function(err, rows) {
                if (err) throw err;
                if (rows) {
                    this.deferred.resolve(rows);
                }
                con.release();
                return this.deferred.promise.done;
            }.bind(this));

            com.Q.nfcall(this.go()).then...

Я нашел несколько статей об адаптации Q к узлу, таких как эта.


person andrsnn    schedule 27.02.2015    source источник
comment
this.go ничего не возвращает...   -  person elclanrs    schedule 27.02.2015
comment
Думаю, меня просто смущает вложенная отложенная функция, которую я пытаюсь написать. Можете ли вы объяснить, как заставить this.go возвращать вложенные отложенные?   -  person andrsnn    schedule 27.02.2015
comment
Создайте обещание внутри go, а не внутри обратного вызова. Вот упрощенный пример: function foo() { function bar() { return 42; }; bar(); } . Вы действительно ожидаете, что foo() вернет 42?   -  person Felix Kling    schedule 27.02.2015
comment
return defer.promise(); Должно быть return defer.promise;   -  person Juan Mendes    schedule 27.02.2015
comment
да, положить defer вне обратного вызова, а затем вернуться в foo, сработало отлично. Также defer.promise. Кто-нибудь, пожалуйста, напишите ответ, и я приму его! Спасибо, парни!   -  person andrsnn    schedule 27.02.2015
comment
Будущим зрителям очень помог этот пример: gist.github.com/jeffcogswell/8257755   -  person andrsnn    schedule 27.02.2015


Ответы (1)


Что-то вроде этого должно делать это («должно», потому что у меня нет возможности проверить это с помощью MySQL)

var Q = require('Q');

function DB(pool) {
    this.asyncQuery = function(sql) {
        return function () { 
            var result = Q.defer(),
                paramsArray = [].slice.call(arguments);
            pool.getConnection(function(err, con) {
                if (err) return result.thenReject(err);
                con.query(sql, paramsArray, result.makeNodeResolver());
                result.promise.finally(function () {
                    con.release();
                });
            });
            return result.promise;
        };
    };
    this.getAllUsers = this.asyncQuery('SELECT * FROM Users');
    this.getUserByName = this.asyncQuery('SELECT * FROM Users WHERE name = ?');
}

module.exports = DB;

Использование:

var DB = require('./DB');
var com = require('./mainRebuild');
var db = new DB(com.pool);

db.getAllUsers().then(function (users) {
    console.log(users);
});

db.getUserByName('JohnDoe').then(function (user) {
    console.log(user);
});

var customQuery = db.asyncQuery('SELECT * FROM foo WHERE id = ?');
customQuery(42).then(function (result) {
    console.log(result);
});
person Tomalak    schedule 27.02.2015
comment
красивая. добавление фабричной функции к module.exports, возвращающей new DB(), позволяет экспортируемому объекту быть полностью цепочечным. :D например. module.exports = createApp; function createApp() { return new DB(); } - person andrsnn; 27.02.2015
comment
Также см. измененный ответ. Определение DB не должно знать, что pool определено в './mainRebuild'. Мы можем ввести это. - person Tomalak; 27.02.2015
comment
Есть ли причина не импортировать зависимость от './mainRebuild'? Является ли инъекция предпочтительнее, чем она лучше? - person andrsnn; 27.02.2015
comment
Ну, это не касается вашего адаптера БД, какое соединение использовать. Вы могли бы (теоретически) иметь несколько баз данных или пулов соединений, передача пула конструктору БД позволяет использовать этот шаблон. - person Tomalak; 27.02.2015
comment
Извините, что выкапываю эту ветку, но мне нравится, как вы разбили этот код на модули, но таким образом соединение не освобождается. Разве это не рискованно? - person krakig; 19.04.2016
comment
@krakig Вы правы, я не думал об этом, когда писал этот ответ. Я добавил обратный вызов finally, который закрывает соединение. - person Tomalak; 19.04.2016