Выбрать главу

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.

Клиент

Клиентская часть веб-сайта по управлению темами состоит из трёх файлов: HTML-страница, таблица стилей и файл JavaScript.