#node.js
#node.js
Вопрос:
Учитывая подобный код из node.js новичок вроде меня:
require('http').createServer(function( req, resp ) {
var pathname = require('url').parse( req.url ).pathname;
if( req.method === "POST" ) {
var postData = "";
req.addListener( "data", function( postDataChunk ) {
postData = postDataChunk;
});
req.addListener( "end", function() {
// do my thing with postData
// and end the response
});
}
}).listen(8888);
… каждый раз, когда поступает POST
запрос, набор анонимных функций, отправляемых слушателям, воссоздается заново.
Это кажется (почти) необходимым, поскольку node не отправляет второй аргумент накопителя "data"
слушателю или аргумент конечного результата "end"
слушателю. Таким образом, анонимным функциям необходимо ссылаться на postData
переменную, чтобы построить результат.
Я предпочитаю, чтобы именованные функции для прослушивателей создавались вне обработчика запросов сервера, вот так:
function data_listener( postDataChunk ) {
this.some_unique_property.accumulator = postDataChunk;
}
function end_listener() {
// do my thing with postData
// and end the response
}
require('http').createServer(function( req, resp ) {
var pathname = require('url').parse( req.url ).pathname;
if( req.method === "POST" ) {
req.some_unique_property = {
response: resp,
pathname: pathname,
accumulator: "",
};
req.addListener( "data", data_listener );
req.addListener( "end", end_listener );
}
}).listen(8888);
Поскольку this
в функциях прослушивателя находится request
объект, я позаимствовал request
объект для накопления данных post, чтобы я мог создавать свои функции один раз и повторно использовать их.
Является ли это приемлемым / безопасным подходом?
Комментарии:
1. Просто
delete req.some_unique_property
в нижней частиend_listener
функции2. @Raynos: Это казалось бы хорошей идеей, хотя я предполагаю, что объект запроса уничтожается после закрытия ответа. Тем не менее, я удалю в любом случае.
3. Он действительно уничтожается, как только уничтожается response. После удаления элемента не сохраняется, что я когда-либо видел. Теперь ссылка может сохраняться, и удаление ссылки не приводит к удалению элемента. Но это семантика указателя.
4. @patrick_dw Вы также можете использовать,
.bind
если функции обратного вызова не выдаютthis
какой-либо полезный объект. Если вы предпочитаете не изменять,req
вы можете просто.bind
добавить некоторые данные в свои обратные вызовы, включая запрос. Хотя это более затратно с точки зрения вычислений5. @Raynos: Да, поскольку
.bind()
это должно было бы происходить при каждом возникающем запросе, я, вероятно, просто использовал бы анонимные функции, прежде чем делать это.
Ответ №1:
Я предпочитаю последний подход по сравнению со многими анонимными функциями, потому что это упрощает организацию кода. Если код для обработки данных станет большим, первый может стать гигантской функцией с анонимными функциями внутри, и его гораздо сложнее поддерживать.
С помощью последнего я могу легко разбить код на отдельные модули, чтобы все было организовано. Это также позволяет мне легко документировать отдельные обработчики, если я использую что-то вроде JSDoc для документации.
Единственная рекомендация, которую я могу дать в отношении последнего, — это некоторый комментарий, указывающий, что this
должно представлять, чтобы кому-то другому, читающему код, не пришлось тратить слишком много времени на обдумывание этого.
Комментарии:
1. Хорошая идея по поводу комментариев. Возможно, я бы сделал ссылку на переменную. Но знаете ли вы, есть ли какие-либо потенциально негативные последствия для этого?
2. @patrick_dw Я бы сказал, что основной негативный эффект будет иметь место в небольших проектах, где, на мой взгляд, проще работать со всей связанной функциональностью в одном месте, чем разбивать ее.
createServer
все, что вам нужно знать о функциональности сервера, находится в одном месте, поэтому вам не нужно бегать к другим файлам / частям файла, чтобы увидеть, где может быть функция-обработчик.3. @patrick_dw Однако, по мере увеличения размера проекта, один гигант
createServer
со всеми перечисленными внутри обработчиками становится более обременительным, чем того стоит, и лучше разделить различные части функциональности.4. @patrick_dw что касается вашей позиции по
req
объекту, я бы лично не рассматривал это как злоупотребление. На мой взгляд, возможность легко расширять объекты в соответствии с вашими потребностями является сильной стороной.5. Хорошо, приятно знать. Я не был уверен, что эти внутренне сгенерированные объекты были священными. Было немного затруднительно добавлять к нему свойство.
Ответ №2:
В этом, вероятно, нет ничего плохого, но звучит ли это как хорошая идея?
Анонимные функции дешевы. Любой текущий движок JS может создавать миллионы таких объектов в секунду, что на несколько порядков превышает достижимую частоту запросов.
Комментарии:
1. Я слышу, что вы говорите, и вы правы, восстановление функций не имеет большого значения. Я просто предпочитаю избегать этого, если это возможно. Звучит ли это как хорошая идея? С точки зрения эффективности, да. С точки зрения этого ноющего ощущения, что я использую объект request не по назначению, нет. Итак, я немного разорван на этом. Хотя это определенно не имеет большого значения.
2. Поскольку анонимные функции дешевы, я ручался за то, что не злоупотреблял объектом запроса, вместо этого используя замыкания в качестве вашего первого примера 🙂
Ответ №3:
А) Я не вижу причин не использовать подобные функции, я думаю, это делает вещи чище, в стеке вызовов уже будет другой уровень, будь то анонимный или выполненный подобным образом, поэтому сделайте код программиста читаемым.
Б) как это не то, для чего используется уровень маршрутизации? Я не вижу разницы. Конечно, я предпочитаю инвертировать мой req-метод и мой path, но они эффективно работают одинаково.
Комментарии:
1. Да, это для уровня маршрутизации. Никаких различий. И я согласен с функциями, но вопрос был больше о целесообразности злоупотребления
request
объектом.2. Злоупотребляйте этим сколько угодно. Это вполне жизнеспособно.
Ответ №4:
Мне это кажется преждевременной оптимизацией. Способ, которым я обычно обрабатываю такого рода вещи, выглядит следующим образом:
require('http').createServer(function( req, res ) {
if( req.method === "POST" ) {
var buf = [];
req.on('data', [].push.bind(buf)).on('end', function(){
// do my thing with buf
});
}
}).listen(8888);
И, кстати, во втором примере вам следует исключить require('url')
.
Ответ №5:
Если вы действительно беспокоитесь о производительности, вы могли бы отредактировать саму библиотеку http, чтобы при необходимости настроить унаследованные объекты запроса.
По-видимому, http-библиотека уже предоставляет вам возможность переопределить ваш класс request, и это должно выглядеть примерно так:
function MyNewRequest( options, cb ){
http.ClientRequest.call(self, options, cb); //Call the original constructor
this.on('data', this.customData ); //Hook the event, and now we are the object
}
util.inherits(MyNewRequest, http.ClientRequest); //Inherit the original object
MyNewRequest.prototype.customData = function(data){
}
var oldRequest = http.request;
http.request = function( options, cb ){ //Override the original ClientRequest provider
if( options.path == "/upload" ) //If it is our special path, return our new object
return new MyNewRequest( options, cb );
return oldRequest( options, cb );
}