Циклы и вложенные обещания

Я использую Request-Promise и cheerio, чтобы очистить некоторые данные веб-сайта, по сути, я пытаюсь достичь следующего:

  1. Создать пустой массив
  2. Авторизоваться
  3. Получите информацию с одной страницы и поместите объекты в массив
  4. Получите информацию с другой страницы и поместите объекты в массив
  5. For each now object in the array, I need to:
    • Go to the URL stored within that object {link: "some url", 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}]}

Шаг 6 - это то, где я сталкиваюсь с проблемами, я не знаю, как это сделать, не вкладывая обещание в цикл 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);
    })

    };

person Chris Talke    schedule 04.04.2018    source источник


Ответы (1)


Самый простой и чистый способ - использовать 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, если это не так, пожалуйста, дайте мне знать.

Я не могу его протестировать, потому что у меня нет вашего полного кода, но он должен работать.

person Marcos Casagrande    schedule 04.04.2018
comment
Большое спасибо за решение, оба решения отлично сработали. В моем случае я перейду к первому методу (async / await), поскольку Promise.all, похоже, выдавал 404 ошибки с конкретного сайта, который я очищаю, который async / await не вызвал (я предполагаю, что для исправления этого мне, вероятно, потребуется перехватить URI, в котором возникла ошибка, и повторить попытку). Еще раз - Большое спасибо, я был на грани отказа! - person Chris Talke; 04.04.2018
comment
Я рада, что тебе помогло! Да, Promise.all работает без сбоев, поэтому обработка ошибок - это боль. Обычно я использую обертку, которая не откажется. Но я бы пошел с async await, если вам не нужно идти параллельно - person Marcos Casagrande; 04.04.2018