2013-11-08 3 views
0

Представьте, что вы хотите, чтобы загрузить изображение или файл, это будет первый способ интернет научит вас идти вперед:Являются ли обратные вызовы для запросов плохой практикой в ​​node.js?

request(url, function(err, res, body) { 
    fs.writeFile(filename, body); 
}); 

Но разве это не аккумулировать все данные в body, заполняя память? Будет ли более pipe?

request(url).pipe(fs.createWriteStream(filename)); 

Или это обрабатывается внутри по аналогичной теме, буферизируя поток в любом случае, делая это несущественным?

Кроме того, если я хочу использовать функцию обратного вызова, но не body (потому что вы все еще можете pipe), будет этот буфер памяти все еще заполнен?

Я спрашиваю, потому что первый метод (callback) позволяет мне загружать файлы, а не запускать их параллельно (*), но я не хочу заполнять буфер, который я тоже не буду использовать. Так что мне нужен обратный вызов, если я не хочу прибегать к чему-то необычному, например async, чтобы использовать queue, чтобы предотвратить это.

(*) Что плохо, потому что если вы просто request слишком много файлов, прежде чем они будут пройдены, асинхронная природа request заставит узел душить до смерти в передозировке событий и потери памяти. Сначала вы получите следующее:

"possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit." 

И при растяжении 500 запросов на подачу 500 заполнит вашу память и узел сбоя. Вот почему вам нужен обратный вызов вместо трубы, поэтому вы знаете, когда начинать следующий файл.

+0

Труба была бы более эффективной, поскольку она будет передавать данные в «WriteStream», поскольку данные доступны. Я не думаю, что последнее утверждение верно. – Bulkan

+0

Кажется, что это так. Попробуйте загрузить 50 документов. Вы получите _ "возможную утечку памяти EventEmitter, обнаружен 11 слушателей. Используйте emitter.setMaxListeners(), чтобы увеличить лимит." _ Загрузите 500, и ваша память заполнит и разрушит узел. Вот почему вам нужен обратный вызов вместо трубы, поэтому вы знаете, когда начинать следующий файл. – Redsandro

+1

Resandro, я думаю, вы запутываете потоковые, обратные вызовы и управление потоком. Это управление потоком, которое будет правильно поддерживать ограничения исходящих подключений (см. Async.queue, async.eachLimit и т. Д.), Но как в парадигме обратного вызова/буферизации, так и в потоковой парадигме вам необходимо управление потоком и управление ресурсами. Эти темы различны. –

ответ

3

Но разве это не накапливает все данные в теле, заполняя память?

Да, многие операции, такие как ваши первые данные буфера памяти в памяти для обработки. Да, это использует память, но она по крайней мере удобна и иногда требуется в зависимости от того, как вы собираетесь обрабатывать эти данные. Если вы хотите загрузить ответ HTTP и проанализировать тело как JSON, это почти всегда выполняется с помощью буферизации, хотя это возможно с помощью потокового анализатора, это намного сложнее и обычно не нужно. Большинство данных JSON недостаточно велики, поэтому потоковая передача является большой победой.

Или это обрабатывается внутренне в аналогичной материи, что делает это несущественным?

Нет, API, которые предоставляют вам целую кучу данных в виде строки, используют буферизацию, а не потоковое.

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

Потоковая передача является приятной, когда позволяют обстоятельства, но это не означает, что что-то не так с буферизацией. Истина буферизирует то, как подавляющее большинство вещей работает большую часть времени. В общей картине потоковая передача всего лишь буферизует 1 кусок за раз и ограничивает их с некоторым пределом размера, который находится в пределах доступных ресурсов. Некоторая часть данных должна пройти через память в какой-то момент, если вы собираетесь ее обрабатывать.

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

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

См. Также substack's rant on streaming/pooling in the hyperquest README.

+1

Вы начали хорошо, но -1 для вашего демона и примеров открытых дверей. Если вы можете устранить свои разочарования по этому вопросу и очевидным операторам ограничения буферизации и предоставить что-то полезное вместо этого, я удалю downvote. – Redsandro

+0

Лучше, хотя я не возражал против хипстерских очков. Заключительная цитата об узком дросселе на самом деле не вопрос, поэтому я задал предыдущий вопрос: «Если я хочу использовать обратный вызов, но не использую тело (потому что вы все еще можете пропустить), будет ли это (буферизованная) память все еще используется (впустую)? »_« Потому что мне нужен обратный вызов, если я не хочу прибегать к чему-то вроде [async] (https://github.com/caolan/async) просто для использования [queue] (https://github.com/caolan/async#queue), чтобы предотвратить это. Я думаю, что ответ да, так что я ищу альтернативу. – Redsandro

+1

Вам не хватает того, что потоки ответа или их буфера не влияют на количество одновременных запросов, которые вы ожидаете. То, что вы хотите сделать, - это понять, когда у вас есть «достаточно» ожидающих запросов и WAIT, прежде чем выпустить больше.Речь идет о контроле того, сколько ввода-вывода вы начинаете, а не буферизируете ли вы или потоки ответов. –

1

Я выяснил решение, которое делает вопросы о памяти нерелевантными (хотя мне все еще интересно).

, если я хочу использовать функцию обратного вызова, но не body (потому что вы все еще можете pipe), будет этот буфер памяти все еще заполнен?

Вам не нужно callback от request() для того, чтобы знать, когда запрос закончен. pipe() закроется, когда stream «концы». Тесное генерирует событие и может быть послушал:

request(url).pipe(fs.createWriteStream(filename)).on('close', function(){   
    next(); 
}); 

Теперь вы можете поставить в очередь все ваши запросы и скачивать файлы один за другим.

Конечно, вы можете вакуумировать интернет, используя 8 параллельных запросов все время с библиотеками, такими как async.queue, но если все, что вы хотите сделать, это получить некоторые файлы с простым скриптом, async, вероятно, будет излишним.

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

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