我想处理一系列数据,其中每个数据的输出可以用作其他数据的输入。
例如:
var batch = [
{"id":"a1","depends":[],"data":{"some":"data a1"}},
{"id":"b1","depends":["a1"],"data":{"some":"data b1"}},
{"id":"b2","depends":["a1"],"data":{"some":"data b2"}},
{"id":"c1","depends":["b1","b2"],"data":{"some":"data c1"}},
{"id":"x1","depends":[],"data":{"some":"data x1"}},
];
这意味着一旦a1
完成,其输出将被发送到b1
和b2
;当这些完成后,它们的输出都将发送到c1
(仅在它们都完成后。 x1
可以与所有a1
、b1
、b2
和c1
并行执行;并且b1
可以与b2
并行执行,因为它们之间没有定义depends
。
在完成c1
和x1
后,因此完成了所有 5 个,应返回所有 5 个的输出。
我们将假设没有定义循环依赖关系,因此是一个有向无环图(DAG(
我想知道如何使用 Q 实现这一点,因为:
- 数据的所有处理都是异步的,因此我需要使用回调或延迟和承诺;我更喜欢后者
- 承诺可以兼作定义图形中边缘的便捷方式
但是,我无法将其通过概念阶段
var doPromises = {};
var doData = function(data, dependsResultsHash, callback) {
//Not real processing, simply echoes input after a delay for async simulation purposes
var out = {
echo: {
data: data,
dependsResultsHash: dependsResultsHash
}
};
setTimeout(function() {
callback(out);
}, 1000);
};
var doLine = function(id, depIds, data) {
var deferred = Q.defer;
var dependsPromises = [];
for (var i = 0; i < depIds.length; ++i) {
var depId = depIds[i];
dependPromise = doPromises[depId];
dependsPromises.push(dependPromise);
}
Q.all(dependsPromises).then(function(dependsResults) {
var dependsResultsHash = {};
for (var i = 0; i < depIds.length; ++i) {
var depId = depIds[i];
var depResult = dependsResults[i];
dependsResultsHash[depId] = depResult;
}
doData(data, dependsResultsHash, function(result) {
deferred.resolve(result);
});
});
return deferred.promise;
}
var doBatch = function(batch) {
var linePromises = [];
for (var i = 0; i < batch.length; ++i) {
var line = batch[i];
var linePromise = doLine(line.id, line.depends, line.data);
linePromises.push(linePromise);
doPromises[line.id] = linePromise;
}
Q.all(linePromises).then(function(lineResults) {
console.log(lineResults);
deferred.resolve(lineResults);
});
};
doBatch(batch);
(请注意,此代码未经测试,我不希望它起作用,只是为了说明我的问题所需的要点。
我想知道:
- 我这样做对吗?我是否完全错过了
Q
图书馆的重点。还是延期和承诺? 我主要关心的是
doData
函数:-- Is the way that I have selected the promises of the lines depended upon from the global list of promises `doPromises` ok? -- Is the way that I have obtained the results of the lines depended upon, and inpterpreted that OK?
使用
doBatch
功能:-- I have a local array for `linePromises` and an external hash for `doPromises`, and I feel that these should be combined. How can I do this correctly?
常规
-- The code above presently assumes that all `deferred`s will eventually keep their `promise`s. What if they fail or throw an exception; how do I make my code more robust in handling this? -- I have used a closure allow acces to `doPromises` in both `doBatch` and `doLine`, and it seems a little odd here, is there a better way to do this?
我创建了一个这样做的库:
qryq 是一个 NodeJs 库,它允许人们表达一系列查询,并并行、按顺序或有向无环图定义它们之间的依赖关系。
我最近制作了一个名为 dagmise 的模块,我计划用它来制作一个使用 promise 作为任务的构建系统。我最终制作了返回承诺的图函数的节点。当访问节点时,将评估该节点上的函数,并且返回的 promise 将取代其作为节点的值。因此,即使多次访问节点,该函数也只执行一次。
我一开始的想法是承诺应该是边缘,但现在我认为将它们放在节点上更简单。否则,你最终会在图中拥有两种对象(节点/状态和边缘/承诺(,这使事情变得有点复杂。
我刚刚为此实现了自己的库:promise-dag。
我对上述替代方案(dagmise
,qryq
(不满意,原因如下:
- 他们强加了一个具体的承诺实施。
promise-dag
与任何 Promise 实现兼容,没有任何代码依赖关系。 - 它们提供了一个流畅的API(链接方法(。我更喜欢面向数据的API,它更具可编程性和透明度,这使得使用普通数据结构函数(
_.extend()
,_.pick()
,...(组合计算图以及添加自定义检测(分析/跟踪/日志记录/等(变得容易。