Как я могу вывести данные до того, как закончу ответ?

Вот мой фрагмент, который я тестировал в Chrome 11 и Firefox 4:

var http = require('http');

http.createServer(function(request, response){
   // Write Headers
   response.writeHead(200);

   // Write Hello World!
   response.write("Hello World!");

   // End Response after 5 seconds
   setTimeout(function(){ 
        response.end(); 
   }, 5000);

}).listen(8000);

Как видите, я выставил таймаут response.end(), чтобы проверить, выводится ли response.write до response.end. Хотя по моему опыту это не так.

Есть ли способ вывести данные перед завершением ответа, что-то вроде отправки данных в пакетах?


person Adam Halasz    schedule 06.06.2011    source источник


Ответы (4)


Если вы измените тип содержимого на текстовый/обычный, например:

// Write Headers
response.writeHead(200, {'Content-Type': 'text/plain'});

тогда firefox сразу покажет содержимое. Кажется, что Chrome по-прежнему буферизируется (если вы напишете еще кучу контента, Chrome сразу его покажет).

person Geoff Chappell    schedule 06.06.2011
comment
Это не работает для меня с Firefox, я получил ответ с двумя строками одним выстрелом в конце тайм-аута. То же самое с ИЕ. - person Enrico Giurin; 04.06.2015
comment
Буферизация вывода также может произойти, если для вывода включено сжатие. Он будет буферизовать все, чтобы всю полезную нагрузку можно было сжать вместе. Если вы используете модуль узла compression, вы можете просто добавить заголовок Cache-Control: no-transform. Это предотвратит compression буферизацию вашего вывода. - person Jonah; 15.11.2016
comment
@EnricoGiurin У меня тоже не сработало. Мне просто нужно было указать тип содержимого как text/html; кодировка=utf-8. Теперь он работает и в Firefox, и в Chrome. Вам даже не нужно устанавливать для передачи-кодирования значение chunked, чтобы оно работало в обоих браузерах. - person FeignMan; 31.08.2017

На самом деле есть способ сделать это без установки Content-Type: text/plain и по-прежнему использовать text/html в качестве Content-Type, но вам нужно указать браузеру ожидать фрагменты данных.

Это можно легко сделать следующим образом:

var http = require('http');

http.createServer(function(request, response) {

    response.setHeader('Connection', 'Transfer-Encoding');
    response.setHeader('Content-Type', 'text/html; charset=utf-8');
    response.setHeader('Transfer-Encoding', 'chunked');

    response.write('hello');

    setTimeout(function() {
        response.write(' world!');
        response.end();
    }, 10000);

}).listen(8888);


Однако вы должны знать, что до тех пор, пока не будет вызван response.end(), запрос все еще выполняется и блокирует другие запросы к вашему серверу nodejs.
Вы можете легко проверить это, открыв вызов этой страницы (localhost: 8888) на две разные вкладки. Один из них будет ждать 10 секунд, а другой получит начало ответа только после окончания первого ответа (это означает, что вы будете ждать 10 секунд до начала ответа и еще 10 секунд до конца ответа, используя этот код).

Вы, вероятно, тоже можете преодолеть это препятствие, запустив пару процессов nodejs и распределив нагрузку между ними, но тогда это становится намного сложнее, и это поток, который следует взять в другом месте... :)

person gillyb    schedule 08.06.2013
comment
Спасибо, это работает для меня! Таким образом, хитрость заключается не в заголовке типа содержимого, а в атрибуте «Transfer-Encoding» в заголовке. - person Enrico Giurin; 04.06.2015
comment
Он не блокируется до response.end! Я то же самое подумал! Но сделайте конечную точку, которая возвращает сегодняшнюю дату, например if (request.url === '/today') { response.end('Today: ' + new Date()); return; }. Затем нажмите http:localhost:8888, а затем нажмите http:localhost:8888/today, и вы увидите, что можете сразу же получить последнюю дату, в то время как другой запрос выполняется в течение 10 секунд. - person JohnnyFun; 24.07.2015
comment
Я думаю, проблема с вашим тестом в том, что хром по-прежнему отказывается отображать проклятый контент, несмотря на ваши заголовки. Я все еще работаю над тем, чтобы полностью понять эту часть, хотя ... - person JohnnyFun; 24.07.2015

Если вы хотите выводить фрагментированный простой текст в Chrome — точно так же, как Firefox делает по умолчанию — вам нужно использовать заголовок 'X-Content-Type-Options': 'nosniff'. См. Что такое X-Content-Type-Options=nosniff?

var http = require('http');

http.createServer(function (req, res) {
    res.writeHead(200, {
        'Content-Type': 'text/plain; charset=utf-8',
        'Transfer-Encoding': 'chunked',
        'X-Content-Type-Options': 'nosniff'});
    res.write('Beginning\n');
    var count = 10;
    var io = setInterval(function() {
        res.write('Doing ' + count.toString() + '\n');
        count--;
        if (count === 0) {
            res.end('Finished\n');
            clearInterval(io);
        }
    }, 1000);
}).listen(8888);

Вам не нужна эта опция, если ваш вывод text/html.

Решение найдено для этого дефекта Chrome: Разбитое кодирование передачи не поддерживается для текстового/простого текста

person Vlad    schedule 07.03.2016
comment
Заголовок nosniff кажется реальным решением. В моем случае мне даже не нужно было устанавливать его на куски. Спасибо! - person laggingreflex; 19.12.2016

Вот основные моменты, на которые необходимо обратить внимание:

  • указать кодировку
  • каждый "чанк" будет выводиться браузером (по крайней мере, то, что я заметил в Chrome) через новую строку (<br>, если кодировка text/html)

Вот так:

res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.write('a<br>');
setTimeout(function() {
  res.write('b<br>');
  setTimeout(function() {
    res.write('c');
    res.end();
  }, 2000);
}, 2000);
person Jürgen Paul    schedule 15.04.2014