如何在数组方法链中展开一个Promise数组?

5

我有一系列的几个 map,其中一个需要针对每个数组元素执行数据库操作,因此我正在使用 async await。

const resultsAsPromises = arr
  .map(syncDouble)
  .map(asyncMapper)

如果它是链中的最后一项,那么这不是一个问题,因为我可以使用Promise.all解开它。

console.log('results', await Promise.all(resultsAsPromises))

然而,我需要执行其他同步操作,因此我希望在继续下一个.map之前解开承诺的值。

有没有办法做到这一点?我想也许只需创建一个提取映射器,例如:

function extractPromiseValues(value) {
  return value.then(v => v);
}

会起作用,但是很遗憾,不行。

var arr = [1, 2, 3, 4, 5];
function timeout(i) {
  return new Promise((resolve) => {
    setTimeout(() => {
      return resolve(`number is ${i}`);
    }, 1);
  });
}

function syncDouble(i) {
  return i * 2;
}

async function asyncMapper(i) {
  return await timeout(i)
}

function extractPromiseValues(value) {
  return value.then(v => v);
}
async function main() {
  const resultsAsPromises = arr
    .map(syncDouble)
    .map(asyncMapper)
//     .map(extractPromiseValues)
  console.log('results', await Promise.all(resultsAsPromises))
}

main();

如何在数组方法链中取消Promise数组的嵌套


我认为这个问题是关于是否可能有一个 .map() 调用链,在某个点上,其中一个返回一个承诺数组,下一个返回从这些承诺解析的值数组。在这种情况下,我不确定下面的答案是否解决了这个问题。你有什么想法吗? - Ghassen Louhaichi
意思是,arr.map(returnValAfter1sec).map(doubleValAfterThat) 可以按照写法实现吗?(其中 returnValAfter1sec 接受一个值并返回一个 Promise,而 doubleValAfterThat 接受一个 Promise 并返回一个值)。 - Ghassen Louhaichi
1
@GhassenLouhaichi,我相信我的编辑已经解决了你的理解问题。你同意吗? - Patrick Roberts
1
@Sphinx 不行,因为 .reduce() 方法的返回值仍然是一个 Promise。无论你采用何种方法,都必须在链的那一部分前面插入 await - Patrick Roberts
@PatrickRoberts 非常感谢,现在它解决了这个问题,我一直在思考并编写一个解决方案,我意识到,就像你所说的那样,无法避免用(await <asyncPart>)<syncPart>包装链的异步部分。 - Ghassen Louhaichi
显示剩余4条评论
1个回答

4
与其将一个身份函数传递给.then(),不如传递您的同步操作,或者在将其传递给同步操作之前,在async函数中使用await等待承诺:
function syncCapitalize(s) {
  return s.slice(0, 1).toUpperCase() + s.slice(1);
}

const resultsAsPromises = arr
  .map(syncDouble)
  .map(asyncMapper)
  .map(p => p.then(syncCapitalize)); // OR
//.map(async p => syncCapitalize(await p));

在您的示例中,这将如下所示:

function timeout(i) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(`number is ${i}`);
    }, 1);
  });
}

function syncDouble(i) {
  return i * 2;
}

function asyncMapper(i) {
  return timeout(i);
}

function syncCapitalize(s) {
  return s.slice(0, 1).toUpperCase() + s.slice(1);
}

async function main() {
  const arr = [1, 2, 3, 4, 5];
  const resultsAsPromises = arr
    .map(syncDouble)
    .map(asyncMapper)
    .map(p => p.then(syncCapitalize)); // OR
  //.map(async p => syncCapitalize(await p));

  console.log('results', await Promise.all(resultsAsPromises));
}

main();

或者,如果我们将问题解释为Ghassen Louhaichi提出的,您可以使用TC39管道操作符(|>)提案之一来编写链式结构,选项如下:

F#管道提案

const results = arr
  .map(syncDouble)
  .map(asyncMapper)
  |> Promise.all
  |> await
  .map(syncCapitalize);

智能流水线提案

const results = (arr
  .map(syncDouble)
  .map(asyncMapper)
  |> await Promise.all(#))
  .map(syncCapitalize);

不幸的是,除非您使用Babel插件,或者在这些提案中有一个被合并到官方ECMAScript规范中,否则您必须用await Promise.all(...)来包装链:

const results = (await Promise.all(arr
  .map(syncDouble)
  .map(asyncMapper)))
  .map(syncCapitalize);

最后,在您的完整示例环境中:

function timeout(i) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(`number is ${i}`);
    }, 1);
  });
}

function syncDouble(i) {
  return i * 2;
}

function asyncMapper(i) {
  return timeout(i);
}

function syncCapitalize(s) {
  return s.slice(0, 1).toUpperCase() + s.slice(1);
}

async function main() {
  const arr = [1, 2, 3, 4, 5];
  const results = (await Promise.all(arr
    .map(syncDouble)
    .map(asyncMapper)))
    .map(syncCapitalize);

  console.log('results', results);
}

main();


太棒了!我们可以在某个地方对这些提案进行投票吗? - Ghassen Louhaichi
只是为了追求严谨,在你最后的代码片段末尾,你只需要加上 console.log('results', resultsAsPromises);,因为结果不再是 promises,事实上,数组的名称是误导性的。 - Ghassen Louhaichi
1
@GhassenLouhaichi 没有一个明确的“投票”地点,但是在Github问题部分 进行了很多有关用例、语法、语义和实现细节的积极辩论。一般来说,某种样式得到的更多关注,就更可能被委员会选中。但是请确保您不会发布毫无意义的问题只为引起关注。 - Patrick Roberts
1
@GhassenLouhaichi 对,谢谢,我漏了那个。是复制粘贴错误。 - Patrick Roberts
1
我认为这非常好。.map(p => p.then(syncCapitalize))正是我在寻找的!我需要深入了解一下这个提案。将其包装在Promise.all中也不错,或许多了一步,但仍然很好。非常感谢Patrick。 - 1252748

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接