для цикла по событию управляемый код? - PullRequest
2 голосов
/ 02 января 2011

В хранилище данных redis у меня есть список ключей, я хочу перебрать этот список ключей и получить эти значения из redis.Подвох в том, что я использую управляемый событиями язык, javascript через node.js

Если бы javascript был процедурным, я мог бы сделать это

function getAll(callback) {
    var list = redis.lrange(lrange('mykey', 0, -1);
    for ( var i = 0; i < list.length; i+= 1 ) {
        list[i] = redis.hgetall(list[i]);
    }
    callback(list);
}

Но я не могу, поэтому ... Я делаю это?

function getAll(callback) {
    redis.lrange('mykey', 0, -1, function(err, reply) {
        // convert reply into messages
        var list = [];
        var index = -1;
        var recurse = function() {
            if ( index == reply.length ) {
                callback(list);
            } else {
                redis.hgetall(reply[i], function(err, reply) {
                    list.push(reply);
                    index += 1;
                    recurse();
                });
            }
        };
        recurse()
    });
};

Это выглядит неправильно, потому что вместо того, чтобы выполнять все запросы сразу, а затем позволять обратным вызовам вставлять в список, я форсирую последовательную последовательность вызовов.Что произойдет, если есть тысячи ключей?

Могу ли я сделать это как-нибудь?

function getAll(callback) {
    redis.lrange('mykey', 0, -1, function(err, reply) {

        // convert reply into messages
        var list = [];
        var insert = function(err, reply) {
            list.push(reply);
        };
        for ( var i = 0; i < reply.length; i += 1 ) {
            redis.hgetall(reply[i], insert);
        }

        ??? how to block until finished ??? 
        callback(list);
    });
};

Ответы [ 3 ]

3 голосов
/ 02 января 2011

???как заблокировать до конца ???

Вы не можете, если ваши звонки асинхронны.Вы должны определить ваш getAll с ожиданием, что он завершится асинхронно, а затем немного его переделать.

Я не знаком с вызовами redis, которые вы делаете, но основной шаблон:

function doTheBigThing(param, callbackWhenDone) {
    asyncCallToGetList(param, function(result) {
        var list = [];

        asyncCallToGetNextEntry(result.thingy, receivedNextEntry);

        function receivedNextEntry(nextResult) {
            if (nextResult.meansWeAreDone) {
                callback(list);
            }
            else {
                list.push(nextResult.info);
                asyncCallToGetNextEntry(result.thingy, receivedNextEntry);
            }
        }
    });
}

Разбить это:

  1. doBigThing (ваш getAll) вызывается.
  2. Он выполняет вызов "get the list", передавая вфункция, используемая в качестве обратного вызова, когда у нас есть список или дескриптор списка или что-то еще.
  3. Этот обратный вызов определяет функцию receivedNextEntry и вызывает функцию «получить следующую запись» с любой информацией, используемой дляполучить запись, передав ее в качестве обратного вызова.
  4. receivedNextEntry сохраняет полученную запись и, если она выполнена, запускает основной обратный вызов «все сделано»;если нет, он отправляет следующий запрос.

Извините, что не могу дать вам ответ, относящийся к redis, но я думаю сопоставления:

  • doBigThing = getAll
  • asyncCallToGetList = redis.lrange
  • asyncCallToGetNextEntry = redis.hgetall

... нокакие параметры вы используете с redis.lrange и redis.hgetall Боюсь, я не знаю.

1 голос
/ 04 января 2011

Если вам часто приходится использовать такие шаблоны, то вам может быть интересно попробовать async.js библиотеку .Используя async.js, вы могли бы написать что-то вроде этого:

function getAll(callback) {
    redis.lrange('mykey', 0, -1, function(err, reply) {
        async.concat(reply, redis.hgetall, callback);
    });
};

Что в основном означает "вызвать hgetall для каждого элемента в 'reply', затем объединить все результаты и передать обратному вызову".

1 голос
/ 02 января 2011

Объявите переменную объекта перед отправкой вызовов в цикле for. Каждый вызов может затем добавить свой результат в объект.

Затем вам нужен код для ожидания всех вызовов. Это может помочь вам: https://gist.github.com/464179

Пример:

function getAll(callback) {

    var results = [];

    var b = new Barrier(2, function() {
        // all complete callback
        callback();
        }, function() {
        // Aborted callback, not used here
    });

    var list = redis.lrange(lrange('mykey', 0, -1);
    for ( var i = 0; i < list.length; i+= 1 ) {
        //dispatch your call
        call(function(foo){
            results.push(foo);
            b.submit();
        });
    }
}

Обратите внимание, что call() должна быть вашей функцией асинхронной базы данных, которая выполняет обратный вызов в результате.

...