Javascript-форум (https://javascript.ru/forum/)
-   Node.JS (https://javascript.ru/forum/node-js-io-js/)
-   -   Массив promise'в (vow) (https://javascript.ru/forum/node-js-io-js/55398-massiv-promise%27v-vow.html)

FoxTrix 26.04.2015 03:47

Массив 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 часа назад, заранее извиняюсь за возможно очевидные косяки =)

Erolast 26.04.2015 07:41

Цитата:

Начал писать парсер html
...зачем? :blink:
https://www.npmjs.com/package/htmlparser2

Цитата:

Использую vow.
Чем не устраивают родные промайзы, раз уж используешь 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

Erolast 26.04.2015 07:44

Цитата:

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:09

А вообще, как-то так делается:

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}`);
});

FoxTrix 26.04.2015 12:07

Цитата:

Сообщение от Erolast

я знаю что есть модули которые парсят html, собственно по коду видно что использую cheerio.
Видимо неправильно выразился, я пишу не сам парсер, а некую утилиту, которая парсит(с помощью готового модуля), и потом обрабатывает результат.

Цитата:

Сообщение от Erolast
Чем не устраивают родные промайзы, раз уж используешь io.js?
https://developer.mozilla.org/en-US/...bjects/Promise
http://www.2ality.com/2014/10/es6-promises-api.html

тем что я понятия не имел что в 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 для '=>'
Может какой-нибудь модуль подключить нужно?

Erolast 26.04.2015 19:49

Цитата:

я понятия не имел что в io.js есть родные промайзы
Только это не в io, а во все js.
Цитата:

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

Цитата:

вываливается с Unexpected token для '=>'
А, да, я ж забыл, стрелочные функции в io.js еще не включены по умолчанию.
Либо запускай ио с флагом --harmony_arrow_functions, либо пропускай код через babel, либо перепеши на обычные функции.

FoxTrix 26.04.2015 23:33

переписал на обычные функции, работает, получилось нечто такое:
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 в том же порядке что вызываются, и не получится что вернулся сначала второй, потом первый, т.к. по второй ссылке сервер ответил быстрее?

Erolast 27.04.2015 09:02

Цитата:

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

Erolast 27.04.2015 09:05

Цитата:

urls.map(parseUrl)
Не надо так делать. Если вдруг захочется добавить в функцию parseUrl дополнительные аргументы, весь код навернется.
urls.map(function(url){return parseUrl(url)})

FoxTrix 27.04.2015 12:52

Цитата:

Сообщение от Erolast
Конечно. Заресолвится тот, где сервер ответил быстрее, а возвратятся так, как запросил.

Спасибо! =)

urls.map(function(url){return parseUrl(url)})

да, я сначала так и переписал, когда пытался ещё i в функцию передать, но потом когда понял что возвращаются объекты по очереди, и второй аргумент не нужен немного сократил =)
если что, пример увидел здесь

Большое спасибо, теперь всё работает как нужно! =)

ps может ещё подскажите в сторону какого модуля можно погуглить, чтобы сделать очередь из запросов (чтобы сервер с 503 ошибкой не выкидывал)?


Часовой пояс GMT +3, время: 04:44.