【问题标题】:Why Array.map() with async / await returns strange results?为什么带有 async / await 的 Array.map() 返回奇怪的结果?
【发布时间】:2019-06-29 13:31:43
【问题描述】:

这是我的代码的工作版本,它按预期返回所有内容:

***.then(r => r.json()).then(async r => {

                    for (let i = 0; i < r.length; i++) {
                        let pipeline = r[i];
                        pipeline.collapsed = true;
                        pipeline.levels = await this.getPipelineLevels(pipeline.id);
                    }

                    this.project.pipelines.items = r;
                })

这是返回奇怪结果的“损坏”版本:

****.then(r => r.json()).then(r => {
                    let pipelines = r.map(async (value) => {
                        let levels = await this.getPipelineLevels(value.id);
                        return {...value, collapsed: true, levels: levels};
                    });

                    this.project.pipelines.levels = pipelines;

console.log(JSON.stringify(pipelines))*.map() 之后出现在控制台中的奇怪输出:

[{"_c":[],"_s":0,"_d":false,"_h":0,"_n":false},{"_c":[],"_s":0,"_d":false,"_h":0,"_n":false}]

这里发生了什么?

【问题讨论】:

  • 因为.map 实际上并不是await 它通过了回调。
  • this.project.pipelines.levels = await Promise.all(pipelines);

标签: javascript vue.js async-await arrow-functions


【解决方案1】:

因为Array.map 实际上并不是await,所以它传递了回调。它不在乎你将它的回调标记为async

只需 Array.map 你的 Promise 然后将它们传递给 Promise.all 并让它等待一切(并行)为你。

const getPipelineLevels = id => new Promise(resolve => {
  setTimeout(() => resolve({ id: id, foo: 'bar' }), 500)
})

const idx = [1,2,3,4,5]

const tasks = idx.map(id => {
  return getPipelineLevels(id)
    .then(value => ({ ...value, bar: 'baz' }))
})

Promise.all(tasks)
  .then(results => {
    console.log(results)
  })

【讨论】:

  • 谢谢,但有很多不必要的代码行 :) @gleam 的建议更适合我。
  • @AndrewShmig Err,什么是“不必要的代码行”?我可以通过 WinZip 把它放到 0 和 1 中,如果这就是你的船的话。
  • 我明白一切,别担心,但这不是我的编码风格。就这样。再次感谢!
  • @AndrewShmig 我认为你不明白。 gleam 之前的回答基本没问题,但并不完全正确。这可能不是“你的风格”,但它是正确的。无论如何,gleam 已经调整了他的,所以它是正确的。
  • 再想一想……你说得对。谢谢!
【解决方案2】:

试试这样的:

.then(async r => {
    let pipelines = await Promise.all(r.map(async (value) => {
        let levels = await this.getPipelineLevels(value.id);
        return {...value, collapsed: true, levels: levels};
    }));
    this.project.pipelines.levels = pipelines;
});

Array.map(async (value) =&gt; {...}) 返回一个 Promise 数组。

这个解决方案也将比 OP 试图实现的更快,因为它是并行等待的。

注意你应该avoid awaiting a .then(…) chain

【讨论】:

  • FWIW 这个解决方案也将比 OP 试图实现的更快,因为它正在等待并行。但实际上.map 中不需要async。只需映射承诺并让Promise.all 等待它们。如果您需要修改 Promise 的值,只需将 .then 附加到 .map 内的每个 Promise 并在那里完成您的工作。
  • @NikKyriakides - 我可以使用您的评论来扩展答案吗?
  • 这并没有实现相同的建议。那是批发剪切和粘贴。
  • @AndrewShmig 因为这个是正确的,所以以前不是。
  • 如果您认为您之前的答案有其自身的优点,请将您的答案回滚到原来的答案,我将取消删除我的答案。不要让我影响你的答案。
猜你喜欢
  • 2019-10-05
  • 2016-09-02
  • 1970-01-01
  • 2020-02-28
  • 1970-01-01
  • 2019-12-11
  • 1970-01-01
  • 2012-08-08
  • 2012-12-09
相关资源
最近更新 更多