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 ошибкой не выкидывал)?

Erolast 27.04.2015 15:35

Цитата:

если что, пример увидел здесь
MSDN? Нашел где смотреть.
https://developer.mozilla.org/en-US/...ects/Array/map - вот здесь получше будет.
Там и предостережение есть.

Цитата:

когда понял что возвращаются объекты по очереди, и второй аргумент не нужен немного сократил =)
Проблема в том, что он может понадобиться в любой момент. Надо ж быть готовым к расширению кода.

Цитата:

сделать очередь из запросов
setInterval?

FoxTrix 27.04.2015 16:46

Хорошо, ещё раз спасибо =)

nick1m 31.10.2015 14:29

Цитата:

Сообщение от Erolast (Сообщение 368472)
setInterval?

Здравствуйте!

Не могу понять, как правильно добавить в promise setInterval, когда внутри него асинхронные функции. Следующий .then не дожидается их выполнения.

Promise.all наверное не подходит, т.к. данные подгружаются внутри setInterval динамически, объём большой, скорее всего будет переполнение буфера. Но даже как его применить, в данном случае, я не могу понять.

nick1m 01.11.2015 03:54

Извините, разобрался, тупил, resolve закопипастил и забыл удалить.


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