【问题标题】:Accumulate promise values "functionally"“功能上”累积承诺值
【发布时间】:2017-04-17 18:43:29
【问题描述】:

说我有这个:

function getAllPromises(key: string, val: any): Promise {

 const subDeps = someHash[key];
 const acc = {}; // accumulated value

 return subDeps.reduce(function (p, k) {

  return p.then(v => getAllPromises(k, v)).then(function (v) {
        acc[k] = v;
        return acc;
   });

 }, Promise.resolve(val));

}

有没有办法避免不得不声明一个单独的变量:

const acc = {}; // accumulated value

并以某种方式从承诺链中返回?

最终,acc 变量是从承诺链中得到解决的,但我想知道我是否可以以某种方式避免不得不在链“外部”声明它。

编辑:类似这样的种子数据如下所示:

  {
    'one': [function (v) {
      return Promise.resolve('one');
    }],

    'two': ['one', function (v) {
      return Promise.resolve('two');
    }],

    'three': ['one', 'two', function (v) {
      return Promise.resolve('three');
    }],

  }

它只是一个声明性依赖树。依赖关系可以并行解决;但在它们运行时可能会有效地阻塞。例如,如果我有这样的功能:

function foo(one,two,three){


}

我想注入这 3 个依赖项。它们可以“并行”采购,但three 将被阻止,直到onetwo 被采购。

subDeps 变量是什么?在 key="three" 的情况下,subDeps['one','two']

【问题讨论】:

  • 所以有几个问题可以很好地回答这个问题:(一个)初始运行subDeps.reduce(暂时忘记递归),你传入初始对象,然后此后,您将传入新的累加器。那么每个子依赖项的element[0] 是否期望父项,其余的期望一个增长的对象? (二)subDeps 的数组实际上是连续的吗?它们是否依赖于按该顺序解析,或者它们是可以并行化的兄弟键?据我猜测,兄弟键不需要按照它们定义的顺序排列,这样更简单。
  • 是的,问题是在递归的任何给定级别上,顶级参数“val”可以是任何东西,正如我用“any”类型指出的那样。
  • 使用 acc 作为自变量,确保存在一个对象,我们可以将键/值附加到该对象。但出于好奇,我只是想知道是否有办法简化它。
  • 但问题是:subDeps[0] 是否真的需要传入的value(而subDeps[1 - n] 不需要)?在考虑您的问题时,看起来我可以重构它,使其不需要keyvalue 作为递归输入,而是需要someHashsubDeps。仅当我可以并行运行 subDeps 而不是等待每个都按顺序完成时,这才是正确的。
  • @Norguard 你是对的,subDeps 可以并行运行;以我现在的方式,它们几乎是串联的。好主意。

标签: javascript node.js es6-promise


【解决方案1】:

在这里,我有点冒险,我认为任何子节点的subDeps 的整个列表都可以并行加载。在深入研究这个问题时,我认为没有理由不这样做。事实上,我能看到的唯一潜在问题是,高于但不低于这一点的某个值可能是一个承诺,因此,您甚至可以从这个特定的递归函数中实现承诺。 ..

但是……

这是我认为合理的重构。如果缺少一些明显的需求,请告诉我。

const appendKeyValue = (dict, [key, value]) => {
  dict[key] = value;
  return dict;
};

const getKeyValuePair = hash => key =>
  getRefactoredPromises(hash, hash[key])
    .then(value => [key, value]);

const getRefactoredPromises = (someHash, subDeps) => {
  return Promise.all(subDeps.map(getKeyValuePair(someHash)))
    .then(pairs => pairs.reduce(appendKeyValue, {}));
};

事实上,如果我对这个重构的看法是正确的,那么你甚至不需要其中的 Promise。它只是变成:

const appendKeyValue = (dict, [key, value]) => {
  dict[key] = value;
  return dict;
};

const getKeyValuePair = hash => key =>
  [key, getRefactoredHash(hash, hash[key])];

const getRefactoredHash = (someHash, subDeps) =>
  subDeps.map(getKeyValuePair(someHash))
         .reduce(appendKeyValue, {});

如果这个调用的根级别恰好是一个承诺,那么此时应该是无关紧要的,除非我遗漏了什么(现在是早上 6:20,我还没有闭上眼睛)。

【讨论】:

  • subDeps 可以并行运行;在手头的实际问题中,一个 subDep 可能依赖于另一个并行运行的 subDep,但由于 Promise 非常好,它会正常运行
  • 为了清楚起见,我在 OP 中添加了更多信息。
【解决方案2】:

您可以将第二个参数的数组传递给.reduce(),在.reduce() 回调中使用析构赋值来获取Promise 并传递对象

return subDeps.reduce(([p, acc], k) => 
         [p.then(v => getAllPromises(k, v)).then(v => Object.assign(acc, {[k]:v}))
         , acc]; 
       }, [Promise.resolve(val), {}]).shift()//.then(result => /* result : acc */)

【讨论】:

  • 不错,我觉得还有更直接的方法
  • @AlexanderMills acc 的预期结果是什么?
  • acc 应该是一个普通的对象哈希,有 0 个或多个键
  • @AlexanderMills 查看更新后的帖子。 .shift() 用于从0 的索引0 中获取.reduce() 返回的结果数组的Promise[0] 也可以链接到 .reduce() 以获取 Promise
  • 为了清楚起见,我向 OP 添加了更多信息,谢谢
猜你喜欢
  • 1970-01-01
  • 2014-10-15
  • 2023-04-01
  • 2018-04-19
  • 1970-01-01
  • 1970-01-01
  • 2017-10-18
  • 1970-01-01
  • 2016-03-16
相关资源
最近更新 更多