Привет, хабровчане! В данном посте я хочу поделиться своим вариантом использования схемы обновления данных, описанной в статье хабраюзера 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() { ... } ... });
Полный код приложения, а также демо, находятся здесь. Писал я его исключительно для себя, но буду рад, если оно пригодится кому-нибудь еще.
Спасибо за внимание.
ссылка на оригинал статьи http://habrahabr.ru/post/176903/
Добавить комментарий