在forEach中执行异步任务

时间:2018-01-19 20:44:20

标签: javascript foreach promise

This article表示由于forEach不能识别异步,因此以下代码无法解决。

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

story.chapterUrls是一个包含网址的数组,getJSON从网址中提取数据。但是上面的代码似乎正在工作,尽管每个getJSON()阻止下一个。 Waht是该代码的问题吗?

1 个答案:

答案 0 :(得分:1)

TL; DR最优解决方案位于底部

正如我之前解释的那样,问题是虽然 if #available(iOS 11.0, *) { NSLayoutConstraint.activate([ scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0), scrollView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 0), scrollView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: 0), scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0) ]) }else { NSLayoutConstraint.activate([ scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0), scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0), scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0), scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0) ]) } 保证了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()回调等待所有章节解析,然后同时添加所有HTML。这样做的缺点是使页面空白比下面的方法稍长一些,但总体上会更快完成,因为所有异步请求都是并行发生的。

.then()

Array#reduce()

使用story.chapterUrls.reduce(async function(promise, chapterUrl) { await promise; const chapter = await getJSON(chapterUrl); addHtmlToPage(chapter.html); }, Promise.resolve()); ,创建一个承诺链,按顺序异步加载每个章节,在每个章节被提取后立即添加HTML。这样做的好处是可以尽快将内容放在页面上,但总体上需要更长的时间,因为它不会并行获取资源。

使用对上述方法的一个棘手的小改动,您可以并行加载资源按顺序显示它们,因为它们在{<1}}加载后加载 / em>获取资源:

Array#reduce()

但是,如果您希望一次性渲染所有内容,而不是让页面空白一点,请使用await方法。