Привет! В данном посте я хочу поделиться своим вариантом использования схемы обновления данных, описанной в статье читателя zim32, в одном простеньком вебдванольном приложении.
Disclaimer: Да не введет вас в заблуждение наличие термина «long polling» в заголовке. Это всего лишь присказка, а сказ о другом.
Суть задачи у меня почти такая же как и в исходном посте: мы находимся на странице вывода фильтра RSS-лент, необходимо обновлять ее содержимое по мере поступления новых данных.
БД у меня не используется. Все новые данные сразу пишутся в файловый кэш в формате JSON. Одновременно с этим создается пустой файл-маркер, отдельный для каждого потока (получателя) данных.
В остальном все то же самое, что и в исходном посте. Клиент (браузер) посылает в цикле запросы к файлу-маркеру. При обнаружении изменения в http-заголовке Etag (или Last-Modified) делается запрос на получение самих данных из JSON кэша. Для снижения активности клиента в то время, когда наша страница не в фокусе, добавляем обработку событий onblur и onfocus объекта window.
В результате часть сценария страницы, отвечающая за обновление данных, выглядит примерно так:
$(function() {
var B_AJAXCACHE = false;
var B_STAMP_ETAG = true;
var urlResult = ''; /* URL JSON-файла данных */
var urlResultStamp = ''; /* URL файла-маркера данных */
var nItems = 0; /* счетчик данных */
var sStampResult = 0; /* последний таймстемп файла-маркера */
var nIntPolling = 120 * 1000; /* интервал между запросами файла-маркера */
var nIdTimer = 0;
var bFocus = true; /* признак нахождения окна стр-цы в фокусе */
var bFocusUpd = false; /* признак появления новых данных при получении окном фокуса */
var nStateSound = 0; /* признак использования звукового уведомления о новых данных */
...
queryPolling();
$(window).focus(function() {
var b = bFocus, b2 = bFocusUpd;
bFocus = true; bFocusUpd = false;
if (b || !b2) return;
nStateSound? queryPolling() : postponePolling();
});
$(window).blur(function() {
bFocus = false;
bFocusUpd = nStateSound? false : Boolean(nIdTimer);
if (!nStateSound && bFocusUpd) stopPolling();
});
/**
* Рекурсивный Ajax запрос файла-маркера
*/
function queryPolling() {
stopPolling();
if (!urlResultStamp) return;
var bOk = false;
queryStaticEx(urlResultStamp, 'text', sStampResult, function(txt, s) {
if (s == sStampResult) return;
if (!bFocus) {
bFocusUpd = true; playSound();
return;
}
sStampResult = s;
bOk = true;
queryResults();
})
.complete(function() {
if (bOk) return;
postponePolling();
});
}
/**
* Отложенный Ajax запрос файла-маркера
*/
function postponePolling() {
nIdTimer= setTimeout(queryPolling, nIntPolling);
}
/**
* Остановка рекурсивных запросов файла-маркера
*/
function stopPolling() {
if (nIdTimer) clearTimeout(nIdTimer); nIdTimer = 0;
}
/**
* Ajax запрос JSON-данных
*/
function queryResults() {
if (!urlResult) return;
var nPrev = nItems;
queryStatic(urlResult, outResults)
.complete(function() {
if (nItems != nPrev) playSound();
postponePolling();
});
}
/**
* Ajax запрос статического ресурса
* @param sUrl - URL ресурса
* @param fnSuccess - функция, вызываемый при успехе запроса
* @return объект Deferred
*/
function queryStatic(sUrl, fnSuccess) {
return $.ajax({
url: sUrl,
type: 'GET',
dataType: 'json',
cache: B_AJAXCACHE,
success: fnSuccess
});
}
/**
* Ajax запрос таймстемпа статического ресурса
* @param sUrl - URL ресурса
* @param sType - тип ресурса
* @param sStamp - последний таймстемп ресурса
* @param fnSuccess - функция, вызываемый при успехе запроса
* @return объект Deferred
*/
function queryStaticEx(sUrl, sType, sStamp, fnSuccess) {
return $.ajax({
url: sUrl,
type: 'GET',
dataType: sType,
cache: B_AJAXCACHE,
beforeSend: function(oXhr) {
if (B_STAMP_ETAG && sStamp != '')
oXhr.setRequestHeader('If-None-Match', sStamp);
},
success: function(data, nStatus, oXhr) {
var s = B_STAMP_ETAG? getStampEtag(oXhr) : getStampStd(oXhr);
if (s) fnSuccess(data, s);
}
});
}
/**
* Получение таймстемпа ресурса из заголовка Last-Modified
* @param oXhr - Xhr объект
*/
function getStampStd(oXhr) {
if (!oXhr) return 0;
var s = oXhr.getResponseHeader('Last-Modified');
return s? Date.parse(s) : 0;
}
/**
* Получение таймстемпа ресурса из заголовка ETag
* @param oXhr - Xhr объект
*/
function getStampEtag(oXhr) {
if (!oXhr) return 0;
var s = oXhr.getResponseHeader('ETag');
return s? s : '';
}
/**
* Вывод JSON-данных
* @param aData - массив данных
*/
function outResults(aData) {
...
}
/**
* Звуковое уведомление о новых данных
*/
function playSound() {
...
}
...
});
Полный код приложения, а также демо, находятся здесь. Писал я его исключительно для себя, но буду рад, если оно пригодится кому-нибудь еще.
Спасибо за внимание.
Автор: xmeoff