循环和嵌套Promise

4

我正在使用Request-Promisecheerio来爬取一些网站数据,基本上我想要实现以下目标:

  1. 创建一个空数组
  2. 登录
  3. 从一个页面获取一些信息并将对象推入数组
  4. 从另一个页面获取一些信息并将对象推入数组
  5. 对于数组中的每个新对象,我需要:
    • 转到存储在该对象中的URL{link: "some url", items: []}
    • 循环遍历该链接中找到的所有项,并将其作为迭代对象中的items数组推送,如下所示:{link: "some url", items: [{item},{item}]}.
  6. 访问完成的orderArray,它应该输出类似于这样的内容:
{link: "some url", items: [{item},{item}]},
{link: "some url", items: [{item},{item}]},
{link: "some url", items: [{item},{item}]}

第六步是我遇到问题的地方,我不知道如何在不嵌套for循环的情况下完成这个步骤,因为我的代码就像下面一样开始变得混乱。有没有人能指点一下我应该怎么做?

这是我现在的代码:

    let orderArray = [];

    rp.post(login)

    .then(function(res1){

        // Login & Set Cookies
        cookieJar = res1.headers['set-cookie'];

        return rp(getOpenOrders);

    })

    .then(function($){

        // Get Some Info from getOpenOrders

        orderArray.push({info});

        return rp(getShippedOrders);

    })

    .then(function($){

        // Get Some Info from getShippedOrders

        orderArray.push({info});

        return orderArray;

    })

    .then(function($){

        // Loop through each object in the orderArray
        for (i = 0,; i < orderArray.length; i++){

            rp(orderArray[I].link)

            .then(function($){

            //Get length of items on page
            let itemsOnPage = $('tbody tr').length;

            //Get some more details for each object
            for (j = 0,; j < items.length; j++) {
                    let moreinfo = {…};
                    orderArray.items.push(moreinfo);
            }

          }
        }

        return orderArray;

    })

    .then(function($){

        // Log finished Array
        console.log(orderArray);

    })

    .catch(function(err){
        console.log(err);
    })

    };
1个回答

3
最简单和最干净的方法是使用 async/await。但是,该代码不会并行运行(除非我们等待 Promise.all)。
.then(async() => {

    // Loop through each object in the orderArray
    for(let i = 0; i < orderArray.length; i++) {

        // Some may argue no await inside loop...
        // We wait for rp to resolve, it looks like
        // synchronous code so it's easy to understand
        const $ = await rp(orderArray[i].link);

        let items = $('tbody tr');

        for(const item of items) {
            let moreinfo = {};
            orderArray[i].items.push(moreinfo);
        }

    }

    return orderArray;
})

你可以使用 Promise.all 并行发送所有请求,并在它们全部完成时处理结果。
.then(() => {

    // Loop through each object in the orderArray

    const requests = [];

    for(const order of orderArray) {
        // Push each promise
        requests.push(
            rp(order.link)
        );
    }

    // This will resolve when every request finishes
    // If one fails, it will reject, going to `.catch`
    return Promise.all(requests); 
})
.then(results => {

    // Results is an array containing each individual request result.

    results.forEach(($, i) => {

        //Get length of items on page
        let items = $('tbody tr');

        //Get some more details for each object
        for(const item of items) {
            let moreinfo = {};
            orderArray[i].items.push(moreinfo);
        }

    })

    return orderArray;

});

我假设rp解析了一个cheerio对象,如果不是,请告诉我。
我无法测试它,因为我没有你的全部代码,但它应该可以工作。

非常感谢您提供的解决方案,两种方法都非常有效。在我的情况下,我将采用第一种方法**(async/await),因为Promise.all似乎会从我正在爬取的特定网站中抛出404错误,而async/await**没有引起这种情况(我认为要解决这个问题,我可能需要.catch URI发生错误并让它再次尝试)。再次感谢您,我差点放弃了! - Chris Talke
很高兴它对你有帮助!是的,Promise.all是一个快速失败的函数,所以错误处理很麻烦。我通常使用一个不会拒绝的包装器。但是,除非必须并行执行,否则我会选择async await。 - Marcos Casagrande

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