Являются ли эти два метода опроса одинаковыми с точки зрения производительности?
Нет, они не одинаковы.Возможно, вы не отправляете запрос на сервер каждые X миллисекунд, но вы опрашиваете XMLHttpRequest#responseText
геттер каждые X миллисекунд, что является непроизводительным расходом независимо от того, как вы на это смотрите.
Хорошей новостью является то, что вам вообще не нужен интервал, потому что обработчик события XMLHttpRequest#onprogress
вызывается каждый раз, когда данные отправляются с сервера.Просто переместите содержимое вашего интервала в обработчик событий onprogress
для запроса, и все готово.
let data;
const request = new XMLHttpRequest();
request.open('GET', '/stream');
request.onprogress = function() {
if(data === request.response) return;
data = request.response;
console.log(data);
};
request.send();
В этом примере опрос не выполняется.Клиент просто обрабатывает события progress
, соответственно обновляя целевой элемент.Если какое-то время обновления не поступают, мы не запускаем интервал каждую секунду, просто чтобы проверить, так что никакого вреда не будет.
Является ли это такой же производительностью, как у сокета?У меня нет ответа на этот вопрос, но я готов поспорить, что это чертовски близко.Достаточно близко, чтобы я мог сказать «достаточно похоже».
Теперь я вижу одно явное различие между этим методом и сокетом в том, что сокеты обеспечивают двустороннюю связь, тогда как я не смог реплицировать отправку данных насервер более одного раза, используя один и тот же запрос.Это может быть возможно, но я еще не понял.
Дальнейшая абстракция
Вы можете абстрагировать это, реализовав класс, расширяющий XMLHttpRequest
вот так:
class ChunkedResponseRequest extends XMLHttpRequest {
constructor(...args) {
super(...args);
this.data = null;
this.addEventListener('progress', _ => {
if(this.data === this.response) return;
const chunk = this.data === null ? this.response
: this.response.substr(this.data.length);
this.data = this.response;
this.dispatchEvent(new MessageEvent('message', { data: chunk }));
});
}
}
Тогда вы можете использовать это так:
const request = new ChunkedResponseRequest();
request.addEventListener('message', function({ data }) {
console.log(data);
});
request.open('GET', '/stream');
request.send();
Значение свойства data
события сообщения будет последним ответом от сервера.
Реализация фрагментированного ответа
Для тех, кто хотел бы воспроизвести это поведение с помощью Node.js (лично я не работаю с Python или Flask)Вот краткий пример сервера, который отвечает бесконечным количеством фрагментов с интервалом в одну секунду:
const http = require('http')
const requestHandler = (request, response) => {
response.setHeader('Content-Type', 'text/html; charset=UTF-8');
response.setHeader('Access-Control-Allow-Origin', '*'); // so we can access locally
response.setHeader('Transfer-Encoding', 'chunked');
(function send(i, delay) {
setTimeout(_ => {
response.write(i + '\r\n');
send(i + 1, 1000);
}, delay)
})(0, 0);
}
const server = http.createServer(requestHandler)
server.listen(3000, (error) => error && console.log(error));
Убедитесь, что для URL XMLHttpRequest
в коде на стороне клиента установлено значение http://localhost:3000
где 3000
- это порт, который прослушивает сервер.Очевидно, что если у вас уже есть что-то, прослушивающее порт 3000
, вам нужно выбрать неиспользуемый порт.