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 的运行时状态有关,我多虑了?