2014-09-03 3 views
0

Я новичок в Node.js и обещает (здесь я использую Q.js). Я пытаюсь сделать скребок из сайта, который имеет следующую структуру:JQuery асинхронные обещания с вложенными циклами

  • main_page: имеет список категорий, каждая категория имеет ссылку, указывающую на странице список магазинов.
  • список магазинов страница: есть список магазинов, каждый магазин имеет ссылку, указывающую на страницу сведений о магазине.
  • магазин подробно страница: есть данные, которые я ищу.

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

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

Как я мог это сделать?

function get(url) { 
    var deferred = Q.defer(); 
    requestify.get(url).then(function(response) { 
     deferred.resolve(cheerio.load(response.getBody())); 
    }); 
    return deferred.promise; 
} 

function process_main_page($) { 
    var promises = []; 
    $('.categories a').each(function(i) { 
     var deferred = Q.defer(); 
     var storesList = $('.store'); 
     get($(this).attr('href')).then(function($) { 
      deferred.resolve(process_stores_list(storesList)); 
     }); 
     promises.push(deferred); 
    }); 
    return Q.all(promises); 
} 

function process_stores_list(storesList) { 
    var promises = []; 
    storesList.each(function() { 

     // Here I need to make another ajax call for each store detail page, which has the data that I need. 

     promises.push(deferred); 
    }); 
    return Q.all(promises); 
} 

function end(res) { 
    var deferred = Q.defer(); 
    fs.writeFile('output.json', JSON.stringify(myGatheredData, null, 4), function(err) { 
     deferred.resolve(function() { 
      res.send('File successfully written! - Check your project directory for the output.json file'); 
     }); 
    }); 
    return deferred.promise; 
} 

app.get('/', function(req, res) { 
    get(url).then(process_main_page).then(end); 
}); 
+1

http: // jsfiddle.net/arunpjohny/v917j5ec/5/ –

+2

Ваш код заполнен отложенным анти-шаблоном :(Подумайте об этом и закрепите его. –

ответ

2

Как @BenjaminGruenbaum уже комментировал, ваш код завален с deferred antipattern. Единственное (более или менее) законное использование Q.defer() для fs.writeFile, но вы забыли обработать ошибки там. Легче всего promisify that API.

Я не могу заставить этот подход работать.

Общая структура кажется прекрасной. Тем не менее, некоторые пункты:

  • Вы, кажется, никогда не выбираете stores_list со страницы с магазинами. Вы получаете эту страницу, но разрешите обещание с var storesList = $('.store'); со страницы категории?
  • Ваш end метод получает myGatheredData - массив результатов, соединенных Q.all - передается как его аргумент. Он не имеет никакого доступа к объекту ponse res.

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

Я думаю, что это причина - вы, вероятно, уже строили отложенные для массива Q.all(), но не разрешали их. Это заставило возвращенное обещание «повесить» (осталось в ожидании), и обратный вызов end так и не был вызван.

var write = Q.nbind(fs.writeFile, fs); 
function get(url) { 
    return requestify.get(url).then(function(response) { 
     return cheerio.load(response.getBody())); 
    }); 
} 

function process_main_page($_main) { 
    var promises = $_main('.categories a').map(function(i) { 
     // var storesList = $_main('.store'); // not sure what this did 
     return get($_main(this).attr('href')).then(process_storelist_page); 
    }).toArray(); 
    return Q.all(promises); 
} 
function process_storelist_page($_stores) { 
    return process_stores_list($_stores('a.store').map(function() { 
     return $_stores(this).attr('href'); // whatever? 
    }).toArray()); 
} 

function process_stores_list(storesList) { 
    var promises = $.map(storesList, function(store_url) { 
     // Here make another ajax call for each store detail page 
     return get(store_url).then(process_store_page);); 
    }); 
    return Q.all(promises); 
} 
function process_store_page($_store) { // which has the data that I need. 
    return /* select some data from the page */; 
} 
function save_data(myGatheredData) { 
    return write('output.json', JSON.stringify(myGatheredData, null, 4)).then(function() { 
     return 'File successfully written! - Check your project directory for the output.json file'; 
     }); 
    }); 
} 

app.get('/', function(req, res) { 
    get(url).then(process_main_page).then(save_data).then(function end(result) { 
     res.send(result); 
    }); 
}); 

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

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