承诺和并发的递归

Recursion with promises and concurrency

请耐心等待,因为这需要一些设置。

为了问题/讨论的目的,考虑一下这个琐碎的价值观树。

const valuetree = [{
    item: 1,
    value: 100,
  },
  {
    item: 2,
    value: [{
      item: 3,
      value: [{
          item: 4,
          value: 200
        },
        {
          item: 5,
          value: 200
        },
        {
          item: 6,
          value: 200
        },
      ]
    }]
  },
];

现在,如果我们想编写一个递归函数来将所有值相加,那么这本身就很简单了。像这样:

function sumTree(leaf) {
  let total = 0;

  if (Array.isArray(leaf)) {
    leaf.forEach(subLeaf => {
      total += sumTree(subLeaf);
    });
  } else {
    if (Array.isArray(leaf.value)) {
      total += sumTree(leaf.value);
    } else {
      total += Number(leaf.value);
    }
  }

  return total;
}

现在假设我们想使用 promises 来实现同样的事情?

这就是我最终得到的...

async function sumTreeAsync(leaf) {
  let promises = [];

  if (Array.isArray(leaf)) {
    leaf.forEach(async subLeaf => {
      promises.push(sumTreeAsync(subLeaf));
    });
  } else {
    if (Array.isArray(leaf.value)) {
      promises.push(sumTreeAsync(leaf.value));
    } else {
      promises.push(leaf.value);
    }
  }

  return promises;
}

那么 returned 看起来像这样

[ Promise { [ 100 ] }, Promise { [ [Promise] ] } ]

这是有道理的,一个包含 2 个顶级项目的数组,一个字面值的承诺,以及一个 return 嵌套承诺数组的承诺。

所以现在像 Promise.all 这样的函数只处理承诺的平面数组,我将不得不递归嵌套的承诺以达到相同的结果。所以现在我必须递归这棵树两次,它闻起来很糟糕。

所以也许我需要 return 解决 subleaf 是数组的 2 个案例中的承诺?

是否值得这样做,或者递归应该是同步的吗?

你离得不远了。如果您将 return 更改为进行 Promise .all 调用,并且将 .then (sum) 更改为一个明显的 sum 函数,这将起作用:

const sum = (ns) => ns .reduce ((a, b) => a + b, 0)

async function sumTreeAsync(leaf) {
  let promises = [];

  if (Array.isArray(leaf)) {
    leaf.forEach(async subLeaf => {
      promises.push(sumTreeAsync(subLeaf));
    });
  } else {
    if (Array.isArray(leaf.value)) {
      promises.push(sumTreeAsync(leaf.value));
    } else {
      promises.push(leaf.value);
    }
  }

  return Promise.all(promises).then(sum);
}

const valuetree = [{item: 1, value: 100}, {item: 2, value: [{item: 3, value: [{item: 4, value: 200}, {item: 5, value: 200}, {item: 6, value: 200}]}]}];

sumTreeAsync (valuetree)
  .then (console .log)

但我肯定会以不同的方式编写这段代码:

const sum = (ns) => ns .reduce ((a, b) => a + b, 0)

const sumTreeAsync = async (leaf) =>
  Promise .all (Array .isArray (leaf)
    ? leaf.map (sumTreeAsync)
    : Array .isArray (leaf .value)
      ? [sumTreeAsync (leaf .value)]
      : [leaf .value]
  ) .then (sum)

const valuetree = [{item: 1, value: 100}, {item: 2, value: [{item: 3, value: [{item: 4, value: 200}, {item: 5, value: 200}, {item: 6, value: 200}]}]}];

sumTreeAsync (valuetree)
  .then (console .log)

不止于此。我可能根本不会写这段代码。 JS 总体上还是一门单线程语言。所以你在这种方法中根本没有并发计算。如果您不是简单地求和,而是将事情交给不同的工人来处理,那么这可能是有道理的。实际上,这感觉像是一个不必要的并发症。