《深入浅出Node.js》中关于「哨兵变量」的问题
发布于 3天前 作者 leiming 293 次浏览 来自 问答

4.3.1 节中 P77~78 ,有如下代码:


这里我们尝试通过原生代码解决“难点2”中为了最终结果的处理而导致可以并行调用但实际只能串行执行的问题。我们的目标是既要享受异步I/O带来的性能提升,也要保持良好的编码风格。这里以渲染页面所需要的模板读取、数据读取和本地化资源读取为例简要介绍一下,相关代码如下:

var count = 0;
var results = {};
var done = function(key, value) {
    results[key] = value;
    count++;
    if (count === 3) {
        // 渲染页面    
        render(results);
    }
};
fs.readFile(template_path, "utf8", function(err, template) {
    done("template", template);
});
db.query(sql, function(err, data) {
    done("data", data);
});
l10n.get(function(err, resources) {
    done("resources", resources);
});

由于多个异步场景中回调函数的执行并不能保证顺序,且回调函数之间互相没有任何交集,所以需要借助一个第三方函数和第三方变量来处理异步协作的结果。通常,我们把这个用于检测次数的变量叫做哨兵变量。聪明的你也许已经想到利用偏函数来处理哨兵变量和第三方函数的关系了,相关代码如下:

var after = function(times, callback) {
    var count = 0,
        results = {};
    return function(key, value) {
        results[key] = value;
        count++;
        if (count === times) {
            callback(results);
        }
    };
};
var done = after(times, render);

上述方案实现了多对一的目的。如果业务继续增长,我们依然可以继续利用发布/订阅方式来完成多对多的方案,相关代码如下:

var emitter = new events.Emitter();
var done = after(times, render);
emitter.on("done", done);
emitter.on("done", other);
fs.readFile(template_path, "utf8", function(err, template) {
    emitter.emit("done", "template", template);
});
db.query(sql, function(err, data) {
    emitter.emit("done", "data", data);
});
l10n.get(function(err, resources) {
    emitter.emit("done", "resources", resources);
});

我的问题是:

若使用 count++if (count === times) {} 来记录收到的「不同的」回调函数的数量,如代码中的 「template、data、resource 回调各一次」。当此类请求处于并发场景时,是否 done 函数 有可能回调的结果不符合预期。例如「两次 template 、一次 data」 后,使得 count = 3 后触发回调。总觉得此处使用 if (Object.keys(results).length === times) {} 更为稳妥。还是说这与 node.js 的运行时状态有关,我多虑了?

6 回复

template, data, resource都只是各自调用了一次,不会有某一个出现调用大于一次的情况啊

考虑 到会出现多次的情况,你可以将自增改为置位,反正根据需求改嘛,这是基本技能

对于他这个例子来说 判断count==3 肯定是没有问题的 因为这些回调只会调用一次
向你说的 if (Object.keys(results).length === times) {} 这样的写法 也没什么问题 可以说是更严谨 但如果说要真的严谨的话 还应该用 闭包 把 resultes 或 count 封装起来 要不程序可以从外部直接修改 resultes 或者 count 这几种写法 主要是取决于你对程序的要求

@gloomyzerg @hwoarangzk 谢谢解答。我考虑的问题是,在运行态的时候,多个请求实例应该会在运行中触发多次 emit 的吧? 比如书中的 请教深入浅出书里的一个例子 请教《深入浅出node.js》中的一个关于解决雪崩的问题 就用队列解决多个实例调用同一段代码的问题。如果多个请求在同一个运行环境中,是否会另一个请求实例 emit 的事件被之前的订阅者消费的情况呢?

一般你的 emitter 都是在某个函数中的,所以不是全局的。你担心的这个被不同请求 emit 的情况应该不会发生。 顺便看看这一课:https://github.com/alsotang/node-lessons/tree/master/lesson4

@leiming 哦 那我理解错你的意思了 你的这个担心 不存在, 就像@alsotang 说的 这段代码只是一个片段 实际的情况来说 它都会被包装起来 其实你这个问题让我想起来 lastInsertId 的问题了 道理是一样的

回到顶部