Асинхронный двойной обратный вызов в цикле NodeJS

Сегодня я перехожу к следующему шагу моего вебскреба!

Я уже зацикливаюсь на массиве URL-адресов с async, и я бы снова зациклился на этом обратном вызове и дождался его выполнения перед перезапуском.

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

Это мой код:

   var getWebData = function(url) {
   var data = [];
   async.eachSeries(url, function(urlSingle, cb) {
      request(urlSingle, function(err, resp, body) {
        if (!err) {
          var $ = cheerio.load(body);
          var categoriesURL = [];
            $('.ombre_menu li').each(function(i, element) {
              $(this).find('.nav_sous-menu_bloc li a').each(function(i, element) {
                categoriesURL.push('https://blabla' + $(this).attr('href'));
              })

              // I WANT TO LOOP on the categoriesURL array HERE

                var jsObject = { name : "", description : "", price: "", categorie: "", liter: "", kilo: "", pricePer: "", quantity: "", capacity: "", promotion: "", scrapingDate : "", url: "" };
                data.push(jsObject);
            })
        }
       cb();
      })
   }, function() {
    // this will rum when loop is done
    var json = JSON.stringify(data);
    fs.writeFile('output.json', JSON.stringify(json, null, 4), function(err) {
        console.log('File successfully written!');
     });
   });
}

getWebData(url);
app.listen('8080');

Кто-нибудь знает, как я могу сделать?

Спасибо


person rastafalow    schedule 23.01.2017    source источник


Ответы (2)


Сделал пару изменений в вашем коде:

  1. Используется .mapSeries вместо .eachSeries. Таким образом, вы можете получить данные из функции итератора в том же порядке, что и входной массив. Означает, что вы получите [4,9] для ввода [2,3] квадратной функции, никогда [9,4]
  2. Разбили код на функции, чтобы каждая функция выполняла одну конкретную задачу
  3. Обработка URL категорий перемещена из цикла 1.
  4. Возвращение рано. Это улучшает читаемость кода. if (err) return callback(err);

function getWebData(url) {

  // Using .mapSeries in place of .eachSeries as you seem to want to get data from iterator function
  async.mapSeries(url, processUrl, function(err, results) {
    // this will rum when loop is done
    var json = JSON.stringify(results);
    fs.writeFile('output.json', JSON.stringify(json, null, 4), function(err) {
      console.error('Error', err);
      console.log('File successfully written!');
    });
  });
}

function processUrl(url, callback) {
  request(url, function(err, resp, body) {
    if (err) // Return simple cases early; Improves code readability
      return callback(err); // or return callback(); -- if you don't want to send error upwards

    var $ = cheerio.load(body);
    var categoriesURL = [];

    $('.ombre_menu li')
      .each(function(i, element) { // loop 1
        $(this)
          .find('.nav_sous-menu_bloc li a')
          .each(function(i, element) { // loop 2
            categoriesURL.push('https://blablablac' + $(this)
              .attr('href'));
          }) // loop 2 end

      }) // loop 1 end
    // I WANT TO LOOP ON THE categoriesURL ARRAY HERE
    // Using .mapSeries in place of .eachSeries for same above reason
    async.mapSeries(categoriesURL, processCategoryUrl, function(err, results) {
      if (err)
        return callback(err);
      // This function is called after process array categoriesURL

      // Do what you want here then call callback provided to this method
      return callback(null, results);
    })
  })
}

function processCategoryUrl(categoryUrl, callback) {
  // Just process categoryUrl here and call callback with error or results
  return callback();
}

getWebData(url);
app.listen('8080');
person Sangharsh    schedule 23.01.2017
comment
Большое спасибо @Sangharsh за ваш ответ, но я не знаю, как использовать этот код... - person rastafalow; 23.01.2017
comment
У вас есть какие-то конкретные сомнения? - person Sangharsh; 23.01.2017
comment
Я попробовал ваш код, заменил urlSingle на url (потому что у меня была ReferenceError: urlSingle не определена), и похоже, что у меня бесконечный цикл. @Sangharsh - person rastafalow; 23.01.2017
comment
Вы вызывали callback из метода processCategoryUrl ? Я пока поставил callback(). Позже замените его своей реализацией. Попробуй. - person Sangharsh; 23.01.2017
comment
Если это успех, я должен сделать обратный вызов()? - person rastafalow; 23.01.2017
comment
Вы всегда должны звонить callback. Обратный вызов обычно принимает первый аргумент как ошибку, а второй как результат. Это единственный способ в асинхронном мире сообщить вызывающей стороне, что функция завершена. - person Sangharsh; 23.01.2017
comment
Большое спасибо @Sangharsh, ваш код идеален, я сейчас редактирую свой вопрос, потому что я бы использовал второй вызов запроса во втором обратном вызове. Надеюсь, ты знаешь, как я могу - person rastafalow; 23.01.2017
comment
Хорошо знать. Пожалуйста, примите ответ, если ваш вопрос решен. - person Sangharsh; 23.01.2017
comment
Можно другой вопрос? Чтобы весь разговор, который у нас тут был, не устарел для зрителей. Кроме того, пожалуйста, прикрепите логи/ошибки, когда ваш второй запрос не работает. - person Sangharsh; 23.01.2017

Вы можете использовать вложенный eachSeries. Как это:

var getWebData = function(url) {
   var data = [];
   async.eachSeries(url, function(urlSingle, cb) {
      request(urlSingle, function(err, resp, body) {
        if (!err) {
          var $ = cheerio.load(body);
          var categoriesURL = [];
            $('.ombre_menu li').each(function(i, element) {
              $(this).find('.nav_sous-menu_bloc li a').each(function(i, element) {
                categoriesURL.push('https://blablablac' + $(this).attr('href'));
              })
              async.eachSeries(caturl, function(categoriesURL, cb2) {
                      //Do whatever you want to do here
                      cb2();
              },  function() {
                 //You can apply if and else for err an according to that you can set your callback responce here
                 cb();
              };
            })
        }
      })
   }, function() {
    // this will rum when loop is done
    var json = JSON.stringify(data);
    fs.writeFile('output.json', JSON.stringify(json, null, 4), function(err) {
        console.log('File successfully written!');
     });
   });
}

getWebData(url);
app.listen('8080');
person Pankaj Jatav    schedule 23.01.2017
comment
Спасибо Панкадж за ваш ответ, я не хочу иметь другую функцию после моего второго цикла. Может быть, я могу взять второй и выпустить cb()? - person rastafalow; 23.01.2017