html текст
All interests
  • All interests
  • Design
  • Food
  • Gadgets
  • Humor
  • News
  • Photo
  • Travel
  • Video
Click to see the next recommended page
Like it
Don't like
Add to Favorites

WXHR: старый добрый XHR со вкусом Web Workers

Бывают ситуации, когда веб-приложению требуется поднять кучу данных с сервера, раскодировать их и отправить дальше по назначению. Примером этому может быть онлайн 3d редактор, где каждая модель может занимать несколько мегабайт в gzip'аном json'e.

Что же делать когда браузер среднего пользователя подвисает на секунду или даже больше при загрузке и распаковке данных?
1. Придумать что-нибудь на flash (я не уверен на 100%, но некоторые браузеры запускают плагины в основном потоке)
2. Загружать данные кусками, обрабатывать кусками.
3. Попросить пользователя сделать апгрэйд компьютера.

Все 3 варианта не очень, правда?

Под катом элегантное решение (без лишних скриптов и дописывания кода приложения) этой проблемы.

Нам на помощь приходят веб воркеры, которые, благо, имеют xhr интерфейс внутри.

Нам необходимо подсолить старый добрый XHR воркерами, но все нужно сделать это так, чтобы скрипты, которые используют старую версию XHR не заметили подмену. А старые браузеры, без поддержки воркеров, работали бы как и раньше.
За основу я возьму скрипт xhr из книги Pro JavaScript Design Patterns (7.03 — XHR factory example).
Наш xhr скрипт должен работать в 3-х режимах: xhr для старых браузеров, хост wxhr, воркер wxhr.

Логика работы будет следующая:
0. Пользователь выполняет xhr.request
1. Если браузер не держит воркеров, то работаем по старому
2. Если браузер держит то, в методе request спавним воркера (wxhr.js) и не выполняем запрос,
2.1 Вешаем воркеру соответствующие события, проксируем воркеру запрос через postMessage
3. Запускается скрипт wxhr.js в качестве воркера, скрипт понимает, что он сейчас работает в режиме воркера и вешает message событие
3.1 Скрипт получает запрос,
3.2 Создает обычный xhr объект,
3.3 Выполняет обычный xhr.request,
3.4 Обрабатывает данные, передает данные вниз своему хосту,
3.5 Хост выполняет callback'и с данными, которые обработал воркер.

Патченный xhr он же wxhr:
/**
 * @fileOverview WXHR Request - Web Worker XHR
 *
 * @example
 * <pre>
 * var myHandler = new global.xhr(true), // enable workers
 *     myHandler2 = new global.xhr(),    // worker mode is disabled by default
 *     data = {
 *         method: 'GET',
 *         url: 'test.txt',
 *         success: function (data, isWorker) {
 *             alert(data + (isWorker ? ' XHR called from Worker' : ' XHR called from Window'));
 *         },
 *         error: function (status) {
 *             alert(status);
 *         }
 *     };
 *      
 * myHandler.request(data);
 * myHandler2.request(data);
 * </pre>
 * 
 * @author azproduction
 */

/**#nocode+*/
(function (global) {
/**#nocode-*/

    // Upgrade 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    var xhr = function (canUseWorkers) {
        this.canUseWorkers = (typeof canUseWorkers === 'undefined') ? false : !!canUseWorkers;
    },
    // detect workers support
    workersSupported = typeof global.Worker !== 'undefined',
    // detect mode
    itIsWindow = typeof global.document !== 'undefined';
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    xhr.prototype = {
        request: function(params) {
            params.method = params.method.toUpperCase() || 'GET';
            params.url = params.url || '';
            params.postVars = params.method !== 'POST' ? (params.postVars || null) : null;
            params.success = params.success || function () {};
            params.error = params.error || function () {};
            
            // Upgrade 2 - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            if (this.canUseWorkers && workersSupported && itIsWindow) { // use advanced wxhr
                var worker;
                // this is bad part mb createObjectURL will save in future
                worker = new global.Worker('wxhr.js'); // <<< bad
                worker.onmessage = function(e) {
                    var data = e.data;
                    // proxy response
                    // @todo delete true parameter in Production!
                    params[data.callback](data.data, true);
                };

                // if worker throws error query fails
                worker.error = function(e) {
                    params.error(0);
                };

                // worker proxy request
                worker.postMessage({
                    method: params.method,
                    url: params.url,
                    postVars: params.postVars
                });
                return;
            } // browser do not support workers or script is already works as Worker
            // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            
            var xhr = (this.createXhrObject())(), self = this;
            xhr.onreadystatechange = function() {
                try {
                    if (xhr.readyState !== 4) {
                        return;
                    }
                    self.processResponse(params, {
                        status: xhr.status,
                        responseText: xhr.responseText,
                        contentType: xhr.getResponseHeader('Content-type')
                    });
                } catch (e) {
                    params.error(xhr.status);
                }
            };
            xhr.open(params.method, params.url, true);
            xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
            xhr.send(params.postVars);
        },
        createXhrObject: function() { // Factory method.
            var methods = [
                function() {
                    return new XMLHttpRequest();
                },
                function() {
                    return new ActiveXObject('Msxml2.XMLHTTP');
                },
                function() {
                    return new ActiveXObject('Microsoft.XMLHTTP');
                }
            ];

            for (var i = 0, len = methods.length; i < len; i++) {
                try {
                    methods[i]();
                } catch(e) {
                    continue;
                }
                // If we reach this point, method[i] worked.
                this.createXhrObject = methods[i]; // Memoize the method.
                return methods[i];
            }

            // If we reach this point, none of the methods worked.
            throw new Error('SimpleHandler: Could not create an XHR object.');
        },
        processResponse: function (params, xhr) {
            if (xhr.status === 200) {
                if (xhr.contentType.match(/^application\/json/)) {
                    params.success(JSON.parse(xhr.responseText));
                } else {
                    params.success(xhr.responseText);
                }
            } else {
                params.error(xhr.status);
            }
        }
    };

    // Upgrade 3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    if (!itIsWindow) {
        // worker mode: listen for requests
        global.addEventListener('message', function(e) {
            var data = e.data;
            // proxy success
            data.success = function (data) {
                global.postMessage({
                    callback: 'success',
                    data: data
                });
            };
            // proxy error
            data.error = function (status) {
                global.postMessage({
                    callback: 'error',
                    data: status
                });
            };
            var xhrRequest = new xhr();
            xhrRequest.request(data);
        }, false);
    } else {
        // script mode: export xhr
        global.xhr = xhr;
    }
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
    // global.xhr = xhr;

/**#nocode+*/
}(this));
/**#nocode-*/

Пример работы: azproduction.ru/wxhr/

Я не советую использовать wxhr где попало, он необходим только в тех случаях, когда необходима обработка большого объема входных данных, в остальных случаях он будет всегда уступать xhr'у из основного потока (спавн воркеров, двойная пересылка данных). В некоторых браузерах, в частности в сафари, postMessage перед отправкой кодирует данные в json, а при получении декодирует. Так, что может получиться ещё хуже, чем с обычным xhr.

Провел тест: запускал 2 одинаковых запроса, минуя кэш, первым слал wxhr вторым xhr. В хроме, фф и сафари в 100% случаях вторым приходил wxhr, в опере в 75% второй был wxhr. Данные 6 байт + заголовки.
Читать дальше
Twitter
Одноклассники
Мой Мир

материал с habrahabr.ru

14

      Add

      You can create thematic collections and keep, for instance, all recipes in one place so you will never lose them.

      No images found
      Previous Next 0 / 0
      500
      • Advertisement
      • Animals
      • Architecture
      • Art
      • Auto
      • Aviation
      • Books
      • Cartoons
      • Celebrities
      • Children
      • Culture
      • Design
      • Economics
      • Education
      • Entertainment
      • Fashion
      • Fitness
      • Food
      • Gadgets
      • Games
      • Health
      • History
      • Hobby
      • Humor
      • Interior
      • Moto
      • Movies
      • Music
      • Nature
      • News
      • Photo
      • Pictures
      • Politics
      • Psychology
      • Science
      • Society
      • Sport
      • Technology
      • Travel
      • Video
      • Weapons
      • Web
      • Work
        Submit
        Valid formats are JPG, PNG, GIF.
        Not more than 5 Мb, please.
        30
        surfingbird.ru/site/
        RSS format guidelines
        500
        • Advertisement
        • Animals
        • Architecture
        • Art
        • Auto
        • Aviation
        • Books
        • Cartoons
        • Celebrities
        • Children
        • Culture
        • Design
        • Economics
        • Education
        • Entertainment
        • Fashion
        • Fitness
        • Food
        • Gadgets
        • Games
        • Health
        • History
        • Hobby
        • Humor
        • Interior
        • Moto
        • Movies
        • Music
        • Nature
        • News
        • Photo
        • Pictures
        • Politics
        • Psychology
        • Science
        • Society
        • Sport
        • Technology
        • Travel
        • Video
        • Weapons
        • Web
        • Work

          Submit

          Thank you! Wait for moderation.

          Тебе это не нравится?

          You can block the domain, tag, user or channel, and we'll stop recommend it to you. You can always unblock them in your settings.

          • skypractic
          • домен habrahabr.ru

          Get a link

          Спасибо, твоя жалоба принята.

          Log on to Surfingbird

          Recover
          Sign up

          or

          Welcome to Surfingbird.com!

          You'll find thousands of interesting pages, photos, and videos inside.
          Join!

          • Personal
            recommendations

          • Stash
            interesting and useful stuff

          • Anywhere,
            anytime

          Do we already know you? Login or restore the password.

          Close

          Add to collection

             

            Facebook

            Ваш профиль на рассмотрении, обновите страницу через несколько секунд

            Facebook

            К сожалению, вы не попадаете под условия акции