26.04.2015, 03:47
|
Аспирант
|
|
Регистрация: 27.07.2012
Сообщений: 73
|
|
Массив promise'в (vow)
Доброго времени суток.
Начал писать парсер html, столкнулся с проблемой ожидания promise'ов.
Использую vow.
Есть некий код:
var promises = [], arr = [];
var parseUrl = function (i, url) {
promises[i] = vow.defer();
request({uri:url,method:'GET',encoding:'binary'},
function (err, res, body) {
var $=cheerio.load(
iconv.encode(
iconv.decode(
new Buffer(body,'binary'),
'win1251'),
'utf8')
);
arr[i] = $(some_selector).text();
console.log(arr[i]);
return promises[i].resolve();
});
}
var someFunc = (function () {
var urls = {0:'some_url'};
for(i in urls) {
parseUrl(i, urls[i]);
}
vow.all(promises).spread(function () {
for(i in urls) {
console.log(arr[i]);
}
});
})();
Собственно проблема в том, vow.all не ждёт ответа, а выводит "undefined", а потом выводится console.log с нужной информацией из функции parseUrl.
Если promises объявить как переменную, то проблема решается, но ссылок будет неопределенное кол-во, и соответственно такой вариант мне не подходит.
В документации описано только:
var defer1 = vow.defer(),
defer2 = vow.defer();
vow.all([defer1.promise(), defer2.promise(), 3])
.then(function(value) {
// value is "[1, 2, 3]" here
});
defer1.resolve(1);
defer2.resolve(2);
что мне не подходит из-за неопределенного кол-ва promise'ов.
Есть ли решение данной проблемы, или стоит присмотреться к другим promise модулям (Q/When)?
ps: io.js поставил 4 часа назад, заранее извиняюсь за возможно очевидные косяки =)
Последний раз редактировалось FoxTrix, 26.04.2015 в 03:50.
|
|
26.04.2015, 07:41
|
|
Профессор
|
|
Регистрация: 24.09.2013
Сообщений: 1,436
|
|
Цитата:
|
Начал писать парсер html
|
...зачем?
https://www.npmjs.com/package/htmlparser2
Чем не устраивают родные промайзы, раз уж используешь io.js?
https://developer.mozilla.org/en-US/...bjects/Promise
http://www.2ality.com/2014/10/es6-promises-api.html
Цитата:
|
promises[i] = vow.defer();
|
Ты определись, у тебя массив, или карта? Массив - это упорядоченный набор элементов, в него добавляют только с помощью push/unshift. Для хранения пар "произвольный ключ - произвольное значение" в ES6 есть специальный тип Map, и он поддерживается как последней нодой, так и io.js
let promises = new Map();
promises.set(1, new Promise());
https://developer.mozilla.org/en-US/...al_Objects/Map
Цитата:
|
Собственно проблема в том, vow.all не ждёт ответа, а выводит "undefined"
|
Не понял. Если бы vow.all возвращал undefined, то у тебя бы на 27-ой строке была ошибка TypeError: vow.all(...) is undefined
|
|
26.04.2015, 07:44
|
|
Профессор
|
|
Регистрация: 24.09.2013
Сообщений: 1,436
|
|
Цитата:
|
var urls = {0:'some_url'};
|
Опять же - у объекта могут быть только строковые ключи, для числовых ключей используются либо карты, либо массивы.
let urls = new Map([
[0, "some_url"]
]);
lets urls = ["some_url"];
Цитата:
|
for(i in urls) {
parseUrl(i, urls[i]);
}
|
Если уж используешь io.js, почему не кошерный for of? Да, и что это за объявление i без var/let? Ты что, без use strict пишешь? В курсе, что эта переменная попадет в глобал?
for (let [i, url] of urls) {
parseUrl(i, url);
}
Последний раз редактировалось Erolast, 26.04.2015 в 08:52.
|
|
26.04.2015, 08:09
|
|
Профессор
|
|
Регистрация: 24.09.2013
Сообщений: 1,436
|
|
А вообще, как-то так делается:
function parseUrl(url) {
return new Promise((resolve, reject) => {
request.get(url, (err, res, body) => {
if (err) {
reject(err);
return;
}
resolve(body);
})
});
}
let urls = ["http://javascript.ru", "http://habrahabr.ru"];
Promise.all(
urls.map((url) => parseUrl(url))
).then((body) => {
console.log(`Response arrived: ${body}`);
}).catch((err) => {
console.log(`Error occured: ${err}`);
});
Последний раз редактировалось Erolast, 26.04.2015 в 08:17.
|
|
26.04.2015, 12:07
|
Аспирант
|
|
Регистрация: 27.07.2012
Сообщений: 73
|
|
я знаю что есть модули которые парсят html, собственно по коду видно что использую cheerio.
Видимо неправильно выразился, я пишу не сам парсер, а некую утилиту, которая парсит(с помощью готового модуля), и потом обрабатывает результат.
тем что я понятия не имел что в io.js есть родные промайзы, т.к. в последней строке написал, что начал его изучать пару часов назад =)
Сообщение от Erolast
|
Не понял. Если бы vow.all возвращал undefined, то у тебя бы на 27-ой строке была ошибка TypeError: vow.all(...) is undefined
|
не сама функция, а console.log() который находится в ней.
Сообщение от Erolast
|
Опять же - у объекта могут быть только строковые ключи, для числовых ключей используются либо карты, либо массивы.
|
но как-то оно работает
Сообщение от Erolast
|
Если уж используешь io.js, почему не кошерный for of? Да, и что это за объявление i без var/let? Ты что, без use strict пишешь? В курсе, что эта переменная попадет в глобал?
|
опять же, т.к. не в курсе про for of.
Про глобал в курсе, тут большая часть кода, это компиляция из нескольких примеров под node.js, переписанная под новый синтаксис vow.
Поэтому пока не разобрался, решил синтаксис не трогать, обратившись к принципу "работает - не трогай".
Попробовал последний пример, вываливается с Unexpected token для '=>'
Может какой-нибудь модуль подключить нужно?
|
|
26.04.2015, 19:49
|
|
Профессор
|
|
Регистрация: 24.09.2013
Сообщений: 1,436
|
|
Цитата:
|
я понятия не имел что в io.js есть родные промайзы
|
Только это не в io, а во все js.
Цитата:
|
но как-то оно работает
|
Приведением ключей к строке - да, будет работать, но лучше использовать вещи по своему назначению.
Цитата:
|
вываливается с Unexpected token для '=>'
|
А, да, я ж забыл, стрелочные функции в io.js еще не включены по умолчанию.
Либо запускай ио с флагом --harmony_arrow_functions, либо пропускай код через babel, либо перепеши на обычные функции.
Последний раз редактировалось Erolast, 27.04.2015 в 08:58.
|
|
26.04.2015, 23:33
|
Аспирант
|
|
Регистрация: 27.07.2012
Сообщений: 73
|
|
переписал на обычные функции, работает, получилось нечто такое:
function parseUrl(url) {
return new Promise(function(resolve, reject) {
request.get(url, function(err, res, body) {
if (err) {
reject(err);
return;
}
var $=cheerio.load(
iconv.encode(
iconv.decode(
new Buffer(body,'binary'),
'win1251'),
'utf8')
);
var obj = {
h1 : $(some_selector).text(),
last : $(some_selector).text()
}
resolve(obj);
})
});
}
var urls = ["some_url", "some_url", "some_url"];
Promise.all(
urls.map(parseUrl)
).then(function(obj) {
console.log(obj);
}).catch(function(err) {
console.log('Error occured: '+err);
});
Правильно ли я понимаю что возвращаются promise в том же порядке что вызываются, и не получится что вернулся сначала второй, потом первый, т.к. по второй ссылке сервер ответил быстрее?
Последний раз редактировалось FoxTrix, 27.04.2015 в 01:54.
|
|
27.04.2015, 09:02
|
|
Профессор
|
|
Регистрация: 24.09.2013
Сообщений: 1,436
|
|
Цитата:
|
Правильно ли я понимаю что возвращаются promise в том же порядке что вызываются, и не получится что вернулся сначала второй, потом первый, т.к. по второй ссылке сервер ответил быстрее?
|
Конечно. Заресолвится тот, где сервер ответил быстрее, а возвратятся так, как запросил.
|
|
27.04.2015, 09:05
|
|
Профессор
|
|
Регистрация: 24.09.2013
Сообщений: 1,436
|
|
Цитата:
|
urls.map(parseUrl)
|
Не надо так делать. Если вдруг захочется добавить в функцию parseUrl дополнительные аргументы, весь код навернется.
urls.map(function(url){return parseUrl(url)})
|
|
27.04.2015, 12:52
|
Аспирант
|
|
Регистрация: 27.07.2012
Сообщений: 73
|
|
Сообщение от Erolast
|
Конечно. Заресолвится тот, где сервер ответил быстрее, а возвратятся так, как запросил.
|
Спасибо! =)
urls.map(function(url){return parseUrl(url)})
да, я сначала так и переписал, когда пытался ещё i в функцию передать, но потом когда понял что возвращаются объекты по очереди, и второй аргумент не нужен немного сократил =)
если что, пример увидел здесь
Большое спасибо, теперь всё работает как нужно! =)
ps может ещё подскажите в сторону какого модуля можно погуглить, чтобы сделать очередь из запросов (чтобы сервер с 503 ошибкой не выкидывал)?
|
|
|
|