【问题标题】:for loop over event driven code?for循环事件驱动代码?
【发布时间】:2011-06-02 13:46:43
【问题描述】:

在 redis 数据存储中,我有一个键列表,我想遍历该键列表并从 redis 获取这些值。问题是我正在使用事件驱动语言,通过 node.js 的 javascript

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

这是错误的,因为我不是一次执行所有请求,然后让回调插入到列表中,而是强制执行顺序调用序列。如果有 1000 个键会怎样?

我可以这样做吗?

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

【问题讨论】:

  • 这两个答案都很棒,Barrier gist 为我解决了问题

标签: javascript asynchronous node.js event-driven event-driven-design


【解决方案1】:

???如何阻止直到完成???

如果您正在进行的调用是异步的,则不能。您必须定义您的 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. 它执行“获取列表”调用,传入函数以在我们拥有列表或列表句柄或其他任何内容时用作回调。
  3. 该回调定义了一个函数receivedNextEntry,并使用用于检索条目的任何信息调用“获取下一个条目”函数,并将其作为回调传递。
  4. receivedNextEntry 存储它获得的条目,如果完成,则触发主“全部完成”回调;如果没有,它会发出下一个请求。

很抱歉不能给你一个特定于 redis 的答案,但我认为映射是:

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

...但是redis.lrangeredis.hgetall 使用的参数是什么,恐怕我不知道。

【讨论】:

  • 谢谢!这绝对有帮助!
【解决方案2】:

在 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() 应该是您的异步​​数据库函数,它在结果上执行回调。

【讨论】:

    【解决方案3】:

    如果您发现自己需要经常使用这样的模式,那么您可能有兴趣尝试async.js library。使用 async.js 你可以这样写:

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

    这基本上意味着“在 'reply' 中对每个项目调用 hgetall,然后连接所有结果并传递给回调”。

    【讨论】:

      猜你喜欢
      • 2014-01-03
      • 1970-01-01
      • 1970-01-01
      • 2011-04-10
      • 2013-09-03
      • 1970-01-01
      • 2021-03-25
      • 2011-10-12
      • 1970-01-01
      相关资源
      最近更新 更多