express.js жизненный цикл объекта запроса / ответа при использовании обратных вызовов

#javascript #express #callback #garbage-collection #lifecycle

#javascript #выразить #обратный вызов #сбор мусора #жизненный цикл

Вопрос:

Пожалуйста, не стесняйтесь разобраться со мной, если это избыточный вопрос. (Я искал столько, сколько мог, прежде чем спрашивать)

Я отчаянно пытаюсь понять жизненный цикл объектов запроса / ответа.

Рассмотрим следующий скелетный код:

 app.get('/', function(req, res) {

  var setCookie = function(err, idCookie) { //callback after cookieSearch in DB
    // ... do something with the result of findCookieInDatabase() (handle error, etc.);
    res.sendfile('index.html');
  }

  var cookie = parseCookie(req.get('Cookie')); //parse and format cookie

  findCookieInDatabase(cookie, afterCookieSearch); //tries to find cookie is DB


  // .. do some content return stuff, etc.
}
  

(Пожалуйста, обратите внимание, что исходный код выполняет намного больше, например, проверяет, существует ли «Cookie» и т. Д.)

Я понимаю, что объекты req и res создаются и в какой-то момент должны быть собраны как мусор. (Можно было бы надеяться)

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

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

Итак, вопрос заключается в следующем: Как javascript (или node.js ) знать, как долго поддерживать «res» в рабочем состоянии и когда можно собирать мусор?

Действительно ли строка res.sendfile в setCookie действует как активная ссылка, потому что она вызывается через findCookieInDatabase() ?

Действительно ли javascript отслеживает все ссылки и поддерживает req и / или res в рабочем состоянии до тех пор, пока какая-либо часть любого вызываемого / вызываемого обратного вызова / вещей жива?

Любая помощь с благодарностью. Спасибо за чтение.

Комментарии:

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

2. Итан, несмотря на то, что ты задавался вопросом об обратном, ты дал мне отличное ключевое слово для начала — closures . Это прояснило значительную часть недостающей информации, так что спасибо. Что мне еще остается для понимания / исследования (о чем в основном и идет речь), так это то, как я могу убедиться, что «req» и «res» будут собираться мусором, и я не делаю чего-то, из-за чего они будут храниться бесконечно. Я не нахожу много об этом, но я буду продолжать копать.

3. Рад это слышать. Я постараюсь добавить ответ, который поможет в дальнейшем ответить на ваш вопрос.

Ответ №1:

Многое происходит с вашим кодом — и вашими предположениями — что указывает мне на то, что вам следует изучить некоторые основы JavaScript. Я бы порекомендовал следующие книги:

И, конечно, моя собственная книга «Веб-разработка с использованием Node и Express«. Хорошо, теперь, когда я разобрался со всеми материалами для чтения, позвольте мне попытаться добраться до сути вашего вопроса.

Когда узел получает HTTP-запрос, он создает объекты req and res (которые начинают свою жизнь как экземпляры http.IncomingMessage and http.ServerResponse соответственно). Предполагаемое назначение этих объектов заключается в том, что они живут столько, сколько выполняет HTTP-запрос. То есть клиент выполняет HTTP-запрос, req res создаются объекты and , происходит куча вещей, и, наконец, вызывается метод on res , который отправляет HTTP-ответ обратно клиенту, и в этот момент объекты больше не нужны.

Из-за асинхронной природы узла req в любой момент времени может быть несколько res объектов and, отличающихся только областью, в которой они находятся. Это может показаться запутанным, но на практике вам никогда не придется беспокоиться об этом: когда вы пишете свой код, вы пишете его так, как если бы вы всегда имели дело с одним HTTP-запросом, а ваша платформа (например, Express) управляет несколькими запросами.

В JavaScript действительно есть сборщик мусора, который в конечном итоге освобождает объекты после того, как их количество ссылок падает до нуля. Таким образом, для любого данного запроса, пока есть ссылка на req объект (например), этот объект не будет освобожден. Вот простой пример экспресс-программы, которая всегда сохраняет каждый запрос (кстати, это ужасная идея):

 var allRequests = [];
app.use(function(req, res, next) {
    allRequests.push(req);
    next();
});
  

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

Как правило, с помощью Express вы будете полагаться на асинхронные функции, которые вызывают обратный вызов после выполнения своей работы. Если функция обратного вызова имеет ссылку на объекты req or res , они не будут освобождены до завершения асинхронной функции и выполнения обратного вызова (и все остальные ссылки, естественно, выходят за рамки). Вот простой пример, который просто генерирует искусственную задержку:

 app.get('/fast', function(req, res) {
    res.send('fast!');
});

app.get('/slow', function(req, res) {
    setTimeout(function() {
        res.send('sloooooow');
    }, 3000);
});
  

Если вы перейдете к /slow , ваш браузер будет вращаться в течение 3 секунд. В другом браузере, если вы обращаетесь /fast к куче раз, вы обнаружите, что он все еще работает. Это потому, что Express создает req res объект and для каждого запроса, ни один из которых не мешает друг другу. Но res объект, связанный с /slow запросом, не освобождается, потому что обратный вызов (который содержит ссылку на этот экземпляр) не выполнен.

В конце концов, я чувствую, что вы слишком много думаете. Конечно, полезно понимать основы, но по большей части подсчет ссылок и сборка мусора в JavaScript — это не то, о чем вам нужно думать или управлять.

Надеюсь, это поможет.

Комментарии:

1. Спасибо, Итан. Очень хороший подробный обзор. Только если создатели модулей выдвинули бы такое. Моя естественная среда обитания — это встроенная область, которая делает меня любопытным. Мне нравится заранее знать о потенциальных источниках проблем (вещах, которые я не должен делать), вместо того, чтобы иметь дело с ними, когда они вызывают ошибку. Например, я только что закончил профилирование / стресс-тестирование своего кода на предмет утечек памяти. Многому научился в этом начинании …

2. Хех, да, я знаю, что ты имеешь в виду. Приятно / важно понимать, как все работает (а не только то, что они работают). Несмотря на то, что большая часть работы, которую я выполняю сегодня, — это высокоуровневая веб-работа, самое интересное, что я когда-либо программировал, — это создание встроенного программного обеспечения. Да здравствует C!