Обновление информации в фоне Revisited, или long polling для ретрограда

от автора

Привет, хабровчане! В данном посте я хочу поделиться своим вариантом использования схемы обновления данных, описанной в статье хабраюзера zim32, в одном простеньком вебдванольном приложении.

Disclaimer: Да не введет вас в заблуждение наличие термина «long polling» в заголовке. Это всего лишь присказка, а сказ о другом.

Суть задачи у меня почти такая же как и в исходном посте: мы находимся на странице вывода фильтра RSS-лент, необходимо обновлять ее содержимое по мере поступления новых данных.

БД у меня не используется. Все новые данные сразу пишутся в файловый кэш в формате JSON. Одновременно с этим создается пустой файл-маркер, отдельный для каждого потока (получателя) данных.

В остальном все то же самое, что и в исходном посте. Клиент (браузер) посылает в цикле запросы к файлу-маркеру. При обнаружении изменения в http-заголовке Etag (или Last-Modified) делается запрос на получение самих данных из JSON кэша. Для снижения активности клиента в то время, когда наша страница не в фокусе, добавляем обработку событий onblur и onfocus объекта window.

В результате часть сценария страницы, отвечающая за обновление данных, выглядит примерно так:

Код Javascript/jQuery

$(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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *