Node.js — обеспечение завершения неблокирующего вызова

Я использую модуль узла pg postgres для взаимодействия с моей базой данных. Код работает без проблем, когда я использую событийный API pg. Ниже приведен пример кода:

Миграция.js

 exports.up = function(logger){
    var pg = require("pg")
        , connectionString = //process.env.CONNECTIONSTRING
        , client = new pg.Client(connectionString);

    client.connect();

    var cmd = "CREATE TABLE users( "
                + "id SERIAL NOT NULL, "
                + "firstName VARCHAR(50) NOT NULL, "
                + "lastName VARCHAR(50) NOT NULL, "
                + "CONSTRAINT pk_userid PRIMARY KEY (id) "
                + ")";

    var query = client.query(cmd);

    query.on("end", function(){
        client.end();
            logger.log("complete");
    });
};

Я использую commander.js, чтобы написать утилиту командной строки для этого сценария миграции; однако неблокирующий вызов фрагмента pg освобождает сценарий интерфейса командной строки для завершения до того, как будут выполнены обновления базы данных. Ниже приведен пример фрагмента командной строки:

Мой клитор

var program = require('commander');

program
    .version('1.0.2');

program
    .command("up")
    .description("Migrates up")
    .action(function(){
        require("../src/migration").up();
    });

// Additional code removed for brevity

Есть ли способ изменить сценарий миграции (или приложения commander.js), чтобы гарантировать, что функция миграции up() завершится до завершения моего сценария cli? Я пытался использовать обратные вызовы, но, похоже, это не работает.

ОБНОВЛЕНИЕ Еще один пример, иллюстрирующий этот момент. Ниже приведен модульный тест (написанный с помощью mocha), касающийся этой проблемы.

Up-test.js

describe("feature", function(){
    it("should finish", function(){
        var logger = {};
        var finished = false;
        logger.log = function(){finished = true;};

        var migration = require("../src/migration.js");
        migration.up(logger);
        assert(finished, "function didn't finish"); // finished is false when this gets called.
    });
});

person JamesEggers    schedule 06.07.2012    source источник
comment
Вы пытались передать обратный вызов client.query() вместо использования query.on()?   -  person joshuapoehls    schedule 07.07.2012
comment
Я пробовал синтаксис обратного вызова pg, передавая обратный вызов в конце, а также добавляя обратный вызов в функцию up() и вызывая ее после client.end(), и в обоих случаях код, вызывающий функцию up(), продолжается, проходит мимо вызова и, в конечном счете, заканчивается раньше, чем up().   -  person JamesEggers    schedule 07.07.2012
comment
Так работает асинхронный код. Если вы хотите дождаться продолжения up, вам нужно, чтобы up() вызывал обратный вызов и продолжал выполнение только с этого обратного вызова.   -  person Jakob Borg    schedule 07.07.2012
comment
Похоже, ваш up-test.js имеет небольшую ошибку: assert(finished); должен быть внутри вашей функции logger.log или внутри чего-то, что не будет выполнено немедленно. Mocha предоставляет вам обратный вызов для асинхронных тестов, который вам также придется использовать: it("should finish", function (completedCallback) { }); см. visionmedia .github.com/mocha/#асинхронный-код   -  person Andrew Dunkman    schedule 07.07.2012


Ответы (2)


Похоже, у вашего исходного сценария есть проблема с program.action(function () {});:

program
    .command("up")
    .description("Migrates up")
    .action(function(){
        require("../src/migration").up();

        // nothing preventing this function from exiting immediately
    });

Я немного порылся в документации command.js и не смог найти ничего, касающегося предоставления обратного вызова вашей программе .action(). Если бы я писал это, код выглядел бы примерно так:

program
    .command("up")
    .description("Migrates up")
    .action(function (completedCallback) {

        require("../src/migration").up(function () {
            // migration is complete
            completedCallback();
        });

    });

Однако ваш скрипт, связанный с postgre, выглядит нормально. Несколько несвязанное замечание может состоять в том, чтобы require("../src/migration").up(); возвращал экземпляр require("events").EventEmitter и генерировал события, которые вы хотели бы видеть, вместо использования переменной logger. Но это просто предпочтения.

person Andrew Dunkman    schedule 06.07.2012

Попробуйте установить таймеры (http://nodejs.org/api/timers.html). Мы делаем это в нашем инструменте Azure CLI, чтобы иметь счетчик, который запускается, когда вы выполняете длительные операции.

Вы можете углубиться в код здесь, чтобы увидеть, как мы это делаем: https://github.com/WindowsAzure/azure-sdk-for-node/blob/master/lib/cli/cli.js. Найдите функцию progress().

person Glenn Block    schedule 07.07.2012