Обещанные соединения ничего не возвращают (JS)

Проблема с обещанными соединениями

Недавно я перевел свое приложение Node с локального компьютера на использование Amazon EC2 для приложения Node и VPN для работы с файлами и MySQL.

Я достаточно узнал об обещаниях, чтобы написать следующий фрагмент соединения (который запускает 3 запроса, прежде чем ответить клиенту), используя Синяя птица. Соединения работали на моей машине, но с настройками MySQL, размещенными в VPN, соединения каждый раз прерывались примерно через 30 секунд после запуска приложения, что, как я понял, вероятно, было связано с тем, что я забыл закрыть их.

РЕДАКТИРОВАТЬ: Судя по комментариям, проблема не в закрытии соединения.

Поэтому я изменил свой сценарий наилучшим из известных мне способов, чтобы закрыть соединения, но с промисами это сбивает с толку. Эта версия подключения не работает. Он не дает сбоев и не вызывает ошибок. Он просто не возвращает никаких результатов на стороне сервера. Я думаю, что моя проблема в том, как я закрыл соединения.

  • Что вызывает проблему?

  • Это закрытие соединения?

  • Если да, то как мне их правильно закрыть?

Моя (упрощенная) попытка подключения к MySQL с Bluebird Promises

var mysql = require('mysql');
var Promise = require('bluebird');
var moment = require('moment');
function createConnection() {
    var connection = mysql.createConnection({
            dateStrings : true,
            host : 'hostname', 
            user : 'username',
            password : 'password', 
            database : 'database' 
        });
    connection = Promise.promisifyAll(connection);
    return connection;
}
function sendGame(req, res, sales, settings, categories, players) {
    var game = new Object();
    game.sales = sales;
    game.players = players;
    game.settings = settings;
    game.categories = categories;
    var JSONgame = JSON.stringify(game);
    console.log("Game: " + JSON.stringify(game, undefined, 4));
}
var retrieveSales = Promise.method(function (username, connection, timeFrame) {
        console.log('User ' + username + ' retrieving sales...');
        var q = 'select * from sales_entries where date BETWEEN ? AND ?';
        return connection.queryAsync(q, timeFrame).then(function (results) {
            return results[0];
        });
    });
var retrieveSettings = Promise.method(function (username, connection) {
        console.log('User ' + username + ' retrieving settings...');
        var q = 'select * from sales_settings';
        return connection.queryAsync(q).then(function (results) {
            return results[0];
        });
    });
var retrieveCategories = Promise.method(function (username, connection) {
        console.log('User ' + username + ' retrieving categories...');
        var q = 'select * from sales_categories';
        return connection.queryAsync(q).then(function (results) {
            return results[0];
        });
    });
var retrievePlayers = Promise.method(function (username, connection) {
        console.log('User ' + username + ' retrieving players...');
        var q = 'select * from users';
        return connection.queryAsync(q).then(function (results) {
            return results[0];
        });
    });
var gameSucceed = Promise.method(function gameSucceed(req, res) {
        var username = req.body.username;
        console.log('User ' + req.body.username + ' retrieving game...');
        var timeFrame = [moment().days(0).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss"), moment().days(6).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss")];
        //var connection = Promise.promisifyAll(createConnection());
        return connection.connectAsync().then(function () {
            console.log('Connection with the MySQL database openned for Game retrieval...');
            return Promise.all([retrieveSales(username, connection, timeFrame), retrieveSettings(username, connection), retrieveCategories(username, connection), retrievePlayers(username, connection)]);
        }).then(function () {
            connection.end(),
            console.log("...Connection with the MySQL database for Game retrieval ended")
        });
    });
function getGameData(req, res) {
    gameSucceed(req, res).spread(function (sales, settings, categories, players) {
        return sendGame(req, res, sales, settings, categories, players);
    });
};
var req = new Object();
var res = new Object();
req.body = {
    "username" : "user123",
    "password" : "password"
}
getGameData(req, res);

Результат консоли

User user123 retrieving game...  
Connection with the MySQL database openned for Game retrieval...
User user123 retrieving sales... 
User user123 retrieving settings... 
User user123 retrieving categories... 
User user123 retrieving players... 
...Connection with the MySQL database for Game retrieval ended 
Game: {}

person Community    schedule 16.05.2014    source источник
comment
Любой, кто просматривает этот вопрос во второй раз, я сделал ошибку, оставив данные о моем подключении к MySQL открытыми. Я изменил их, но не хотел, чтобы информация была доступна через просмотр редактирования, поэтому я удалил и повторно опубликовал вопрос.   -  person    schedule 16.05.2014
comment
WTH вы используете две библиотеки обещаний вместе? В Bluebird должно быть все, что вам нужно. Я имею в виду, ты даже нигде не используешь Q?   -  person Bergi    schedule 16.05.2014
comment
@Bergi Берги Я немного запутался в том, что делает каждая библиотека. Немного того и другого было предложено в качестве решения различных проблем, с которыми я столкнулся, работая над различными аспектами приложения.   -  person    schedule 16.05.2014
comment
Возможно, я использую Q в другом месте приложения — это всего лишь часть. Я удалю Q из вопроса. @Берги   -  person    schedule 16.05.2014
comment
На самом деле они должны делать то же самое, и Bluebird предлагает все методы, которые есть и у Q. Если нет, не стесняйтесь задавать вопрос о его преобразовании; даже если они должны хорошо работать вместе, было бы чище использовать только один и менее громоздкий, не требующий никаких преобразований.   -  person Bergi    schedule 16.05.2014
comment
Я предполагаю, что JSONgame пропускает var и что вы дважды вызываете Promise.promisifyAll() при соединении, это только пережитки вашего упрощения? Если это так, пожалуйста, исправьте их.   -  person Bergi    schedule 16.05.2014
comment
@ Берги готово. Поэтому я забываю вернуться из обратных вызовов... Я смотрю на это   -  person    schedule 16.05.2014
comment
Я ненавижу задавать подобные вопросы, потому что даже при всем упрощении, которое я могу сделать, они все равно не могут продемонстрировать фундаментальную ошибку, которая, вероятно, поможет другим зрителям в будущем.   -  person    schedule 16.05.2014
comment
@jt0dd: Нет проблем, я выделил ошибочную часть в своем ответе :-)   -  person Bergi    schedule 16.05.2014
comment
@Берги большое спасибо. На самом деле это было слишком много кода. Спасибо за ваше время.   -  person    schedule 16.05.2014


Ответы (2)


В вашем коде есть определенная проблема с управлением ресурсами, как выразился Берги. Вы должны постоянно помнить, когда закрывать коллекцию, а когда нет.

Оптимальным решением было бы использовать Promise.using однако это доступно только в ветке Bluebird v2, поэтому вам придется немного подождать.

До тех пор вы можете создать свой собственный метод-оболочку, который выполняет более базовое управление ресурсами с ограниченной областью действия:

function connect(fn,timeout){
    timeout = (timeout === undefined) ? 8000 : timeout; // connection timeout
    return createConnection().then(function(connection){
        // run the function, when it resolves - close the connection
        // set a 7 second timeout on the connection
        return fn(connection).timeout(timeout).finally(function(){  
             connection.end();
        });
    });
}

Что позволит вам сделать:

connect(function(connection){
    return gameSucceed(req,resp,connection); // connection is injected to that fn now
}).then(function(val){
      // gameSucceed resolution value here
});

Теперь, когда gameSucceed будет выполнено, соединение автоматически закроется. Это сделало бы сам gameSucceed похожим на:

var gameSucceed = Promise.method(function gameSucceed(req, res,connection) {
    var username = req.body.username;
    console.log('User ' + req.body.username + ' retrieving game...');
    var timeFrame = [moment().days(0).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss"), moment().days(6).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss")];
    return connection.connectAsync().then(function () {
        console.log('Connection with the MySQL database openned for Game retrieval...');
        return Promise.all([retrieveSales(username, connection, timeFrame), retrieveSettings(username, connection), retrieveCategories(username, connection), retrievePlayers(username, connection)]);
    }); // no longer its responsibility to handle the connection
});

Как правило, вы также можете рассмотреть более ООП-стиль кодирования для своего кода.

Удачи и счастливого кодирования.

person Benjamin Gruenbaum    schedule 16.05.2014
comment
Спасибо, чувак, и у меня есть вопрос об этом первом блоке кода. Я не понимаю, как работает .timeout(timeout). Я думаю, я бы понял, что если бы тайм-аут был функцией, а не (timeout === undefined) ? 8000 : timeout, чтобы я мог узнать об этом, можете ли вы дать ссылку на документацию о том, что такое (x == y) ? t : a? Я никогда не использовал эту технику. - person ; 17.05.2014
comment
@ jt0dd, конечно, это особая функция Bluebird, которая автоматически отклоняет обещание, если прошло более timeout миллисекунды - идея состоит в том, чтобы «прикрыть спину», чтобы, если вы забудете отклонить или принять содержащееся обещание - вы получите громкий гнев TimeoutError, чтобы уведомить вас об этом, это личный контакт - и его удаление будет работать так же хорошо, только без этой функциональности. - person Benjamin Gruenbaum; 17.05.2014
comment
Вы говорите о ветке v2 библиотеки промисов, как будто это общеизвестно. Я мог видеть, что опытный веб-разработчик обычно знает немного о том, что будет в следующем выпуске JQuery, но Bluebird v2.x? Откуда вы это знаете? - person ; 17.05.2014
comment
@ jt0dd Я очень вовлечен в процесс разработки Bluebird, у меня есть несколько запросов на включение, и я часто говорю об этом с автором основной библиотеки (@Esailija в Stack Overflow) и другими участниками. Я был (и до сих пор остаюсь) одним из основных сторонников Promise.using в Bluebird в первую очередь, поэтому всякий раз, когда я вижу вопросы, интересующиеся этим видом управления ресурсами (что очень важно для некоторых языков, но безумно сложно ответить правильно себя иногда, например, это) Я немного волнуюсь :) - person Benjamin Gruenbaum; 17.05.2014
comment
Ну, я действительно ценю ваш вклад в игру. Так держать. Круто, работаю над таким проектом. Очень круто - person ; 17.05.2014
comment
Я пробовал и ваш способ, и способ Берги, но я, должно быть, неправильно использую ваш, потому что получаю сообщение об ошибке createConnection() не имеет метода then jsfiddle.net/MVFv4/1 - person ; 22.05.2014
comment
Привет, сэр, не могли бы вы помочь мне по этой теме? stackoverflow.com/q/38802095/6599627 спасибо - person mahdi pishguy; 06.08.2016

var gameSucceed = function gameSucceed(req, res) {
    …
    var connection = createConnection());
    return connection.connectAsync().then(function () {
        return Promise.all([…]);
    }).then(function () {
        connection.end();
    });
};

Обещание, которое в конечном итоге возвращается из этого метода, не имеет значения разрешения. Он создается тем then вызовом, из обратного вызова которого вы не return - что приведет к undefined. Чтобы исправить это, просто направьте результат через:

.then(function(results) {
    connection.end();
    return results;
});

Однако, если вы сделаете это так, соединение не будет закрыто в случае ошибки. Лучшее решение — использовать метод finally(), который работает как предложение finally в синхронном коде. Его обратный вызов будет вызываться как для разрешений, так и для отклонений, и полученное обещание автоматически сохранит значение.

.finally(function() {
    connection.end();
})
// .then(function(results) { })
person Bergi    schedule 16.05.2014