Как сделать несколько вложенных HTTP-запросов с обещаниями и async / await при просмотре веб-страниц - PullRequest
0 голосов
/ 14 апреля 2020

Ниже у меня есть функция Node.js, которая выполняет серию запросов к различным URL-адресам, затем для каждого URL-адреса я использую библиотеку Cheerio web scraping, чтобы l oop просматривал элементы в dom и создавал подмассив. В конце каждого запроса (после того, как вложенный массив заполнен) я хотел бы вывести содержимое этого массива в больший массив, который находится за пределами области действия запроса.

Подход, который я пробую, похоже, не работает. Похоже, у меня нет доступа к «allPlayers» из блока .then.

function readPlayers(teamUrls){

    const allPlayers = [];

    teamUrls.forEach((teamUrl, i) => { 

        const options = {
            gzip: true,
            uri: teamUrl,
            Connection: 'keep-alive',
            transform: function (body) {
                return cheerio.load(body);
            }
        };

        request(options)
        .then(($) => {

            const team = [];

                $('tbody').children('tr').each(function(j, element){            

                     const playerName = $(element).children('td').eq(1).children('span').eq(1).find('a').text().trim();

                     const player = { 'playerName': playerName };

                     team.push(player);

                 });

            allPlayers.push(team);

        }).catch(err => console.log("error: " + err)) );

    });

}

Поэтому мне интересно, как лучше переписать этот код, чтобы заставить запросы работать, и заполнить внешний массив (allPlayers) результатами.

Я пытался вытолкнуть sh весь запрос напрямую во внешний массив, но безрезультатно.

В этом примере я использую запрос-обещание, чтобы сделать запрос ,

Я изучил использование Promise.map, которое, я думаю, подходит для этой ситуации. Тогда я бы вернул весь запрос (я думаю), но я не совсем понимаю, что я делаю в этом случае ... или если он будет работать.

Может ли кто-нибудь объяснить область применения в этом случае, почему я не могу сделать это, как я пытаюсь.

Большое спасибо

Ответы [ 2 ]

0 голосов
/ 14 апреля 2020

Если вы используете forEach, каждый обратный вызов будет выполняться асинхронно, и вы не сможете их ждать. Вы можете поменять его на a для l oop, собрать свои обещания в массив и затем дождаться завершения всех из них:

async function readPlayers(teamUrls) {
  const allPlayers = [];
  const allPromises = [];

  for (var i = 0; i < teamUrls.length; i++) {
    var teamUrl = teamUrls[i];

    const options = {
      gzip: true,
      uri: teamUrl,
      Connection: "keep-alive",
      transform: function(body) {
        return cheerio.load(body);
      }
    };

    allPromises.push(
      request(options)
        .then($ => {
          const team = [];
          $("tbody")
            .children("tr")
            .each(function(j, element) {
              const playerName = $(element)
                .children("td")
                .eq(1)
                .children("span")
                .eq(1)
                .find("a")
                .text()
                .trim();
              const player = { playerName: playerName };
              team.push(player);
            });

          allPlayers.push(team);
        })
        .catch(err => console.log("error: " + err))
    );

    // wait untill all the promises resolve
    await Promise.all(allPromises);

    console.log(allPlayers);

    return allPlayers;
  }
}

Затем вы можете получить всех игроков, ожидая выполнения вашей функции:

var allPlayers = await readPlayers(teamUrls);
0 голосов
/ 14 апреля 2020

Вы должны помнить, что при использовании асинхронной функции вы не можете go вернуться к синхронному выполнению кода.

Это один из способов, которым вы можете это сделать. Он будет извлекать всех игроков параллельно:

async function readPlayers(teamUrls) {
   const playerPromises = teamUrls.map((teamUrl, i) => {
    const options = {
      gzip: true,
      uri: teamUrl,
      Connection: 'keep-alive',
      transform: function(body) {
        return cheerio.load(body);
      }
    };
    return request(options)
  });

  const players = await Promise.all(playerPromises);
  return players.reduce((allPlayers, $) =>{
    const team = [];
    $('tbody').children('tr').each(function(j, element) {
      const playerName = $(element).children('td').eq(1).children('span').eq(1).find('a').text().trim();
      const player = { playerName: playerName };
      team.push(player);
    });
    allPlayers.push(team);
    return allPlayers;
  },[])
}

И вы можете использовать его, используя await readPlayers(array) или readPlayers(array).then(allteamplayers=>{...})

Примечание: В текущем коде это будет быть двумерным массивом, [[{p1: p1} ..], [{p2: p2} ..]] et c

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...