Книга: Выразительный JavaScript
Поддержка длинных запросов
Поддержка длинных запросов
Самый интересный аспект сервера – часть, которая поддерживает длинные запросы. Когда на адрес /talks
поступает запрос GET
, это может быть простой запрос всех тем, или запрос на обновления с параметром changesSince
.
Есть много различных ситуаций, в которых нам нужно отправить клиенту список тем, поэтому мы сначала определим вспомогательную функцию, присоединяющую поле serverTime
к таким ответам.
function sendTalks(talks, response) {
respondJSON(response, 200, {
serverTime: Date.now(),
talks: talks
});
}
Обработчик должен посмотреть на все параметры запроса в его URL, чтобы проверить, не задан ли параметр changesSince
. Если дать функции parse
модуля “url”
второй аргумент значения true
, он также распарсит вторую часть URL – query
, часть запроса. У возвращаемого объекта будет свойство query
, в котором будет ещё один объект, с именами и значениями параметров.
router.add("GET", /^/talks$/, function(request, response) {
var query = require("url").parse(request.url, true).query;
if (query.changesSince == null) {
var list = [];
for (var title in talks)
list.push(talks[title]);
sendTalks(list, response);
} else {
var since = Number(query.changesSince);
if (isNaN(since)) {
respond(response, 400, "Invalid parameter");
} else {
var changed = getChangedTalks(since);
if (changed.length > 0)
sendTalks(changed, response);
else
waitForChanges(since, response);
}
}
});
При отсутствии параметра changesSince
обработчик просто строит список всех тем и возвращает его.
Иначе, сперва надо проверить параметр changeSince
на предмет того, что это число. Функция getChangedTalks
, которую мы вскоре определим, возвращает массив изменённых тем с некоего заданного времени. Если она возвращает пустой массив, то серверу нечего возвращать клиенту, так что он сохраняет объект response
(при помощи waitForChanges
), чтобы ответить попозже.
var waiting = [];
function waitForChanges(since, response) {
var waiter = {since: since, response: response};
waiting.push(waiter);
setTimeout(function() {
var found = waiting.indexOf(waiter);
if (found > -1) {
waiting.splice(found, 1);
sendTalks([], response);
}
}, 90 * 1000);
}
Метод splice
используется для вырезания куска массива. Ему задаётся индекс и количество элементов, и он изменяет массив, удаляя это количество элементов после заданного индекса. В этом случае мы удаляем один элемент – объект, ждущий ответ, чей индекс мы узнали через indexOf
. Если вы передадите дополнительные аргументы в splice
, их значения будут вставлены в массив на заданной позиции, и заместят удалённые элементы.
Когда объект response
сохранён в массиве waiting
, задаётся таймаут. После 90 секунд он проверяет, ждёт ли ещё запрос, и если да – отправляет пустой ответ и удаляет его из массива waiting
.
Чтобы найти именно те темы, которые сменились после заданного времени, нам надо отслеживать историю изменений. Регистрация изменения при помощи registerChange
запомнит это изменение, вместе с текущим временем, в массиве changes
. Когда случается изменение, это значит – есть новые данные, поэтому всем ждущим запросам можно немедленно ответить.
var changes = [];
function registerChange(title) {
changes.push({title: title, time: Date.now()});
waiting.forEach(function(waiter) {
sendTalks(getChangedTalks(waiter.since), waiter.response);
});
waiting = [];
}
Наконец, getChangedTalks
использует массив changes
, чтобы построить массив изменившихся тем, включая объекты со свойством deleted
для тем, которых уже не существует. При построении массива getChangedTalks
должна убедиться, что одна и та же тема не включается дважды, так как тема могла измениться несколько раз с заданного момента времени.
function getChangedTalks(since) {
var found = [];
function alreadySeen(title) {
return found.some(function(f) {return f.title == title;});
}
for (var i = changes.length - 1; i >= 0; i--) {
var change = changes[i];
if (change.time <= since)
break;
else if (alreadySeen(change.title))
continue;
else if (change.title in talks)
found.push(talks[change.title]);
else
found.push({title: change.title, deleted: true});
}
return found;
}
Вот и всё с кодом сервера. Запуск написанного кода даст вам сервер, работающий на порту 8000, который выдаёт файлы из публичной поддиректории и управляет интерфейсом тем по адресу /talks
.
- Улучшенный оптимизатор запросов
- Асинхронный сервер и отмена выполняющихся запросов
- Поддержка SMP
- 1. Оператор Select – базовый оператор языка структурированных запросов
- 2. Унарные операции на языке структурированных запросов
- 3. Бинарные операции на языке структурированных запросов
- 4. Использование подзапросов
- Обработка запросов с помощью PHP
- Рекламно-информационная поддержка
- 6.1.5. Поддержка WINS
- Лекция 11. История запросов
- 6.3. Импорт запросов на перенос