【问题标题】:execution of async task in forEach在 forEach 中执行异步任务
【发布时间】:2018-01-19 20:44:20
【问题描述】:

This article 说下面的代码无法工作,因为forEach 不支持异步。

story.chapterUrls.forEach(function(chapterUrl) {
    getJSON(chapterUrl).then(function(chapter) {
        addHtmlToPage(chapter.html);
    });
})

story.chapterUrls 是一个包含 url 的数组,getJSON 从 url 中获取数据。然而,尽管每个 getJSON() 阻止下一个,但上面的代码似乎正在工作。这段代码有什么问题?

【问题讨论】:

  • 你是什么意思它阻止了下一个?根据你刚才引用的文章,他们没有。问题是因为它们不会互相阻塞,addHtmlToPage() 可以按任何顺序调用。
  • @PatrickRoberts 在forEach 中,执行顺序类似于getJSON(url[0]) -> getJSON(url[1]) -> getJSON(url[2]) ...并且每个都是非阻塞的,因为getJSON() 返回承诺。是这样吗??
  • 正确,非阻塞。但是,处理这些请求的服务器端逻辑当然有可能被阻塞,从而迫使它们处于 FIFO 顺序。这也有可能只是巧合,如果你运行足够多次,它们最终会出现故障。
  • 谢谢。我已经使用返回非常小的数据的 api 测试了代码,所以我认为每个 getJSON 都会阻止下一个。
  • @KevinB 如果您想“投反对票”,标记为重复,或者以其他方式解决问题,解释为stackoverflow.com/questions/48349699/…

标签: javascript foreach promise


【解决方案1】:

TL;DR 最优解在底部

问题,正如我之前解释的,虽然getJSON() 的执行顺序是由forEach() 保证的,但每个.then() 的回调的执行顺序是不保证的,因为它是异步执行的。

解决此问题的两种典型方法是使用Promise.all() 或Array#reduce():

Promise.all():

Promise.all(story.chapterUrls.map(function(chapterUrl) {
  return getJSON(chapterUrl);
})).then(chapters => chapters.forEach(function(chapter) {
  addHtmlToPage(chapter.html);
});

使用Promise.all(),.then() 回调等待所有章节解析,然后同时添加所有 HTML。这样做的缺点是留空页面比下面的方法长一点,但总体上会更快完成,因为所有异步请求都是并行发生的。

Array#reduce():

story.chapterUrls.reduce(async function(promise, chapterUrl) {
  await promise;
  const chapter = await getJSON(chapterUrl);
  addHtmlToPage(chapter.html);
}, Promise.resolve());

使用Array#reduce(),创建了一个承诺链,它按顺序异步加载每一章,在获取每一章后立即添加 HTML。这样做的好处是可以尽快将内容放在页面上,但总体上会花费更长的时间,因为它不会并行获取资源。

对上述方法稍作改动,您可以并行加载资源并按承诺链上的awaiting 加载时按顺序显示它们之后 获取资源:

story.chapterUrls.reduce(async function(promise, chapterUrl) {
  const chapter = await getJSON(chapterUrl);
  await promise;
  addHtmlToPage(chapter.html);
}, Promise.resolve());

但是,如果您想一次性渲染所有内容,而代价是让页面保持更长的空白,请使用Promise.all() 方法。

【讨论】:

  • 第二次调用reduce()没有返回值时,promise参数的值是多少?
  • async 函数返回一个承诺,该承诺在函数完成异步执行时解决。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-11
  • 2018-08-18
相关资源
最近更新 更多