5

我想处理一系列数据,每个数据的输出都可以用作其他数据的输入。

例如:

    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完成,它的输出将被发送到b1b2; 并且当这些完成时,它们的两个输出都将被发送到c1(仅在它们都完成时。 x1可以与所有a1, b1,b2c1; 并行执行,并且b1可以与 并行执行b2,因为depends它们之间没有定义。

c1和完成后x1,因此所有 5 个都完成后,应返回所有 5 个的输出。

我们将假设没有定义循环依赖,因此是有向无环图(DAG)

我想知道如何使用Q来实现它,因为:

  • 所有数据的处理都是异步的,因此我需要使用回调,或者延迟和承诺;我更喜欢后者
  • Promise 可以作为一种方便的方式来定义图中的边

但是,我无法通过概念阶段

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?
    
4

3 回答 3

1

我创建了一个执行此操作的库:

qryq是一个 NodeJs 库,它允许人们以并行、顺序或有向无环图的方式表达一系列查询并定义它们之间的依赖关系。

于 2014-02-26T00:05:43.730 回答
1

我最近制作了一个名为dagmise的模块,我打算用它来制作一个使用 Promise 作为任务的构建系统。我最终制作了返回承诺的图形函数的节点。当一个节点被访问时,它的函数被评估并且返回的promise代替它作为节点的值。所以即使一个节点被多次访问,该函数也只会执行一次。

我的想法是 promise 应该是边缘,但现在我认为将它们放在节点上更简单。否则,您实际上最终会在图表中拥有两种对象(节点/状态和边/承诺),这会使事情变得有些复杂。

于 2015-05-28T00:54:50.890 回答
1

我刚刚为此实现了自己的库:promise-dag

由于以下原因,我对上述替代方案 ( dagmise, )不满意:qryq

  • 他们强加了一个具体的 Promise 实现。promise-dag兼容任何 Promise 实现,没有任何代码依赖。
  • 它们提供了流畅的 API(链接方法)。我更喜欢面向数据的 API,它更具可编程性和透明性,可以使用普通的数据结构函数(、、...)轻松组合计算图_.extend()_.pick()以及添加自定义工具(分析 / 跟踪 / 日志记录 / 等) .)。
于 2016-12-06T15:11:54.543 回答