如何处理依赖于另一个承诺的承诺循环

我是 JavaScript 新手,我在使用 promise 时遇到了麻烦。我正在使用 cloudcraper 来检索网页的 html 以从中抓取数据。我有一个简单的函数 - getData() - 它调用 cloudcraper.get() 并将 html 传递给 extract() 函数,该函数负责抓取数据。这是工作代码:


const getData = function(pageUrl) {

  var data;

  return cloudscraper.get(pageUrl)

    .then(function(html) {

      data = extract(html);

      return data;  

    })

    .catch(function(err) {

      // handle error

    })

}

返回的“数据”对象包含我想要连接的 URL 数组,以便检索其他信息。该信息必须存储在同一个数据对象中。所以我想为数组中包含的每个 URL 再次调用 cloudcraper.get() 方法。我试过下面的代码:


const getData = function(pageUrl) {

  var data;

  // first cloudscraper call:

  // retrieve main html

  return cloudscraper.get(pageUrl)

    .then(function(html) {

      // scrape data from it

      data = extract(html);

      for (let i = 0; i < data.array.length; ++i) {

        // for each URL scraped, call cloudscraper

        // to retrieve other data

        return cloudscraper.get(data.array[i])

          .then(function(newHtml) {

            // get other data with cheerio

            // and stores it in the same array

            data.array[i] = getNewData(newHtml);

          })

          .catch(function(err) {

            // handle error

          }) 

        }

        return data;  

      })

    .catch(function(err) {

      // handle error

    })

}

但它不起作用,因为在解决循环中的承诺之前返回数据对象。我知道可能有一个简单的解决方案,但我无法弄清楚,所以你能帮我吗?提前致谢。


MM们
浏览 160回答 2
2回答

12345678_0001

这可以通过使用Promise.all, 和await/async如果我的理解是正确的,您正在尝试执行以下步骤:获取原始 HTML提取一些 HTML(看起来你想要更多的 url)对于提取的每个网址,您要重新调用&nbsp;cloudscraper将每次调用的结果放回到原始数据对象中。const getData = async (pageUrl) => {&nbsp; &nbsp; const html = await cloudscraper.get(pageUrl);&nbsp; &nbsp; const data = extractHtml(html);&nbsp; &nbsp; const promises = data.array.map( d => cloudscraper.get(d));&nbsp; &nbsp; const results = await Promise.all(promises);&nbsp; &nbsp; // If you wanted to map the results back into the originaly data object&nbsp; &nbsp; data.array.forEach( (a, idx) => a = results[idx] );&nbsp; &nbsp; return data;};

湖上湖

避免此类问题的最佳方法是使用async/await,如评论中建议的那样。这是基于您的代码的示例:const getData = async function(pageUrl) {&nbsp; var data;&nbsp; // first cloudscraper call:&nbsp; // retrieve main html&nbsp; try {&nbsp; &nbsp; const html = await cloudscraper.get(pageUrl);&nbsp; &nbsp; // scrape data from it&nbsp; &nbsp; data = extract(html);&nbsp; &nbsp; for (let i = 0; i < data.array.length; ++i) {&nbsp; &nbsp; &nbsp; // for each URL scraped, call cloudscraper&nbsp; &nbsp; &nbsp; // to retrieve other data&nbsp; &nbsp; &nbsp; const newHtml = await cloudscraper.get(data.array[i]);&nbsp; &nbsp; &nbsp; // get other data with cheerio&nbsp; &nbsp; &nbsp; // and stores it in the same array&nbsp; &nbsp; &nbsp; data.array[i] = getNewData(newHtml); // if getNewData is also async, you need to add await&nbsp; &nbsp; }&nbsp; } catch (error) {&nbsp; &nbsp; // handle error&nbsp; }&nbsp; return data;}// You can call getData with .then().catch() outside of async functions&nbsp;// and with await inside async functions
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript