2016-04-04 2 views
2

У меня есть функция, которая использует модуль cherio для получения данных с веб-сайта.Iterate over async function

Теперь я хотел бы повторять эту функцию через массив ключевых слов, собирать промежуточные результаты в массив с именем статистики и, наконец, распечатать результаты массива статистики на консоль с помощью console.log()

Всякий раз, когда я запускаю этот скрипт, он быстро запускает функцию async и печатает пустой массив статистики.

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

У меня много googled и искал переполнение стека. Кажется, есть много способов достичь моей цели, но что является самым идиоматичным способом в этом узле?

Вот как я ее решил:

var request = require("request"), 
    cheerio = require("cheerio"), 
    base_url = "http://de.indeed.com/Jobs?q="; // after equal sign for instance: sinatra&l= 

/* search syntax: 
    - http://de.indeed.com/Jobs?q=node&l=berlin&radius=100 
    - 
    - 
*/ 

// // 
var search_words = ["django", "python", "flask", 
        "rails", "ruby", 
      "node", "javascript", "angularjs", "react", 
      "java", "grails", "groovy", 
      "php", "symfony", "laravel" 
      ]; 

var counter = 0; 
var stats = []; 


function getStats(keyword) { 
    url = base_url + keyword + "&l="; 
    request(url, function(err, resp, body) { 
    if(!err) { 
     $ = cheerio.load(body); 
     data = $("#searchCount")[0].children[0].data.split(" ").reverse()[0]; 

     stats.push([keyword, data]); 
     counter++; 
    } 
    // list complete? 
    if (counter === search_words.length) { 
     console.log(stats); 
    } 
    }); 
} 

for (var j=0; j<= search_words.length; j++) { 
    getStats(search_words[j]); 
} 
+0

Я только что запустил ваш код, и он отлично работает. Конечно, это нехорошее решение, но хорошо работает. –

ответ

3

Promise - лучшее решение для обработки асинхронных операций.

Promise.all(search_words.map(function(keyword) { 
 
    return new Promise(function(resolve, reject) { 
 
    request(base_url + keyword + "&l=", function(err, resp, body) { 
 
     if (err) { 
 
     return reject(err); 
 
     } 
 
     $ = cheerio.load(body); 
 
     resolve([keyword, $("#searchCount")[0].children[0].data.split(" ").reverse()[0]]); 
 
    }); 
 
    }); 
 
})).then(function(stats) { 
 
    console.log(stats); 
 
});

+0

Это мне очень нравится! Спасибо. – Ugur

+0

Кстати: Что именно аргументы разрешают и отвергают? Я вижу, что они называются функциями. «return reject (err)»; и "resolve ([keyword ..." . Но если эти два аргумента являются самими функциями: где они определены? Я имею в виду, где на самом деле добавляется статистика массива? – Ugur

+0

@Ugur Я бы предложил вам прочитать это статью для получения дополнительной информации о Promise http://www.html5rocks.com/en/tutorials/es6/promises/ – Lewis

0

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

Попробуйте Google для «nodejs синхронного» дает в результате некоторой ссылки на nodejs модулей и/или методологию для записи синхронного кода в nodejs, но я полагаю, что они являются ПОЛЕЗНЫМИ только в какой-то конкретная проблеме (никогда не использовал их сами)

+0

Благодарим вас за ответ. Я знаю, что они асинхронны. - Я хотел бы знать, какой самый идиоматический способ решить эту проблему. - Мне нужно вложить две функции? – Ugur

+0

В принципе да.Или вы можете использовать некоторые модули nodejs (например, synchronize.js или node-sync), которые позволяют сериализовать вызовы. Но, как я уже сказал, я никогда не использовал ни один из этих модулей самостоятельно, поэтому я действительно не знаю, что они соответствуют вашей проблеме. – Gianluca

+0

Теперь, как бы я мог вложить итерацию и асинхронную функцию? Какая итерация позволяет мне ждать результата обратного вызова? – Ugur

1

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

npm install --save q 

Затем используйте его в коде:

var Q = require('q'); 
var requestFn = q.denodeify(request); 

Затем вы перебираете свои значения:

var promises = search_words.map(function(keyword) { 
    url = base_url + keyword + "&l="; 
    return requestFn(url); 
}); 

Q.all(promises).then(function(values) { 
    //values will contain the returned values from all requests (in array form) 
}, function(rejects) { 
    //rejected promises (with errors, for example) land here 
}); 

Функция denodeify от Q в основном превращает функцию обратного вызова в функцию, которая возвращает обещание (шаг за шагом для будущего значения, как только оно есть). Эта функция requestFn (найдите лучшее имя для нее!). Все эти обещания собираются в одном массиве, который передается Q.all, чтобы убедиться, что все обещания выполнены (если кто-то отвергнут, другие обещания также отклонены).

Если это не ваше намеренное поведение: есть множество способов играть с отличной библиотекой Q. См. Документацию: https://github.com/kriskowal/q

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

+0

Lucas, спасибо. Отлично! – Ugur

+0

Upvoting будет оценен, если вам это нравится;) –

+0

Хотелось бы. Но stackoverflow говорит, что мне нужно как минимум 15 очков репутации, чтобы сделать это – Ugur