2013-06-04 3 views
0

В Stack есть несколько подобных вопросов, но я не могу получить ответы на все вопросы, я совершенно новичок в Node и идею асинхронного программирования, поэтому, пожалуйста, несите меня.Создание асинхронного цикла while в узлеJS

Я строю скребок, который в настоящее время имеет процесс 4 шага:

  1. Я даю ей коллекцию ссылок
  2. Он идет к каждому из этих ссылок, находит все соответствующие img src на странице
  3. Он находит ссылку «следующая страница», получает ее href, извлекает дом из указанного href и повторяет шаг №2.
  4. Все эти img src помещаются в массив и вернулся

Вот код. getLinks можно назвать асинхронно, но while цикл в нем в настоящее время не может:

function scrape(url, oncomplete) { 
    console.log("Scrape Function: " + url); 
    request(url, function(err, resp, body) { 
     if (err) { 
      console.log(UHOH); 
      throw err; 
     } 
     var html = cheerio.load(body); 
     oncomplete(html); 
    } 
    ); 
} 
function getLinks(url, prodURL, baseURL, next_select) { 
    var urls = []; 
    while(url) { 
     console.log("GetLinks Indexing: " + url); 
     var html = scrape(url, function(data) { 
      $ = data; 
      $(prodURL).each(function() { 
       var theHref = $(this).attr('href'); 
       urls.push(baseURL + theHref); 
      } 
      ); 
      next = $(next_select).first().attr('href'); 
      url = next ? baseurl + next : null; 
     } 
     ); 
    } 
    console.log(urls); 
    return urls; 
} 

В настоящее время это входит в бесконечный цикл, не очищая ничего. Если я положил url = next ? baseurl + next : null; за пределы обратного вызова, я получаю ошибку "next" is not defined.

Любые идеи о том, как я могу переделать это, чтобы сделать его удобным для узлов? Похоже, что по этой самой природе проблема должна быть блокирующей, нет?

+3

Повторите цикл while как рекурсивную функцию. Затем вы легко узнаете, как сделать это асинхронным путем передачи обратных вызовов. – Bergi

+1

@ Bergi Ah, оказывается, мне просто нужно было знать имя, и я мог бы сам это решить. Для тех, кто наткнулся на это: http://tech.richardrodger.com/2011/04/21/node-js-%E2%80%93-how-to-write-a-for-loop-with-callbacks/является хорошим ресурсом – Jascination

ответ

1

Это частый шаблон, в котором вы хотите выполнить цикл, но использовать асинхронную функцию с обратным вызовом. Поскольку вы не можете ждать асинхронной функции, вы не можете просто использовать цикл while.

Одним из решений является использование «стека» (или массива). Заполните это начальными элементами, которые вы хотите обработать. Когда вы обнаружите больше элементов для обработки, добавьте их в этот стек. И рекурсивно вызовите вашу функцию, увеличивая индекс для обработки до тех пор, пока индекс не будет превышать длину массива.

например.

function do_scrape(stack, this_url, callback) { 
    // get list of URLs from webpage at this_url 
    ... 
    stack.push(new_url); // adding new element to array 
    ... 
    ... 
    callback(); // process callback 
} 

function process_stack(stack_of_urls, idx) { 
    var this_url = stack_of_urls[idx]; 

    do_scrape(
     stack_of_urls, 
     this_url,   
     function() { 
      if (idx + 1 < stack_of_urls.length) { 
       process_stack(stack_of_urls, (idx + 1)); 
      } else { 
       process.exit(0); 
      } 
     } 
    ); 
} 

var stack_of_urls = [ "http://www.yahoo.com/" ]; 
process_stack(stack_of_urls, 0); // start from index zero 

Примечание. Есть много способов приблизиться к этому. Для эффективности вы можете удалить обработанные элементы из стека. Также у вас есть выбор: обрабатывать ли стек от начала до конца или заканчивать до начала и т. Д. Наконец, обратите внимание, что если вы не вызываете асинхронную функцию в своей функции do_scrape, тогда у вас будет замкнутый цикл обратных вызовов, посредством чего узел .js прекратит жаловаться на слишком большой стек.

Смежные вопросы