$30 off During Our Annual Pro Sale. View Details »

ШРИ - JavaScript Асинхронность

ШРИ - JavaScript Асинхронность

Mikhail Davydov

October 11, 2012
Tweet

More Decks by Mikhail Davydov

Other Decks in Education

Transcript

  1. View Slide

  2. Михаил Давыдов
    Разработчик JavaScript
    JavaScript
    Асинхронность

    View Slide

  3. 3
    Задача
    •  Качаем 1 файл
    •  После отправляем данные на 2 сервера
    •  Синхронизируемся

    View Slide

  4. 4
    Сделаем обертку над XMLHttpRequest
    function syncXHR(method, url, data) {
    var xhr = new XMLHttpRequest();
    xhr.open(method, url, false);
    xhr.send(data);
    return xhr.responseText;
    }
    Синхронный код
    var data = syncXHR('GET', ‘http://host1/page.json’);
    data = processData(data);
    syncXHR(‘POST’, ‘http://host2/result/’, data);
    syncXHR(‘POST’, ‘http://host3/result/’, data);
    alert(‘Done!’);

    View Slide

  5. 5
    Схема загрузки
    время
    Блокировка
    Блокировка Блокировка
    Запрос Запрос Запрос
    Подготовка Обработка Отправка Алерт

    View Slide

  6. 6
    Сделаем обертку над XMLHttpRequest
    Асинхронный код
    function asyncXHR(method, url, data, callback) {
    var xhr = new XMLHttpRequest();
    xhr.open(method, url, true);
    xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
    if (xhr.status === 200) {
    callback(null, xhr.responseText);
    } else {
    callback(‘error’);
    }
    }
    }
    xhr.send(data);
    }

    View Slide

  7. 7
    Сам код. Изменилось все.
    Асинхронный код
    asyncXHR ('GET', ‘http://host1/page.json’, null,
    function (err, data) {
    data = processData(data);
    var counter = 2;
    function done(err, data) {
    counter--;
    if (!counter) alert(‘Done!’);
    }
    asyncXHR(‘POST’, ‘http://host2/result/’, data, done);
    asyncXHR(‘POST’, ‘http://host3/result/’, data, done);
    });

    View Slide

  8. 8
    Схема загрузки
    время
    Ожидание Ожидание
    Запрос
    Подготовка Обработка Отправка Алерт

    View Slide

  9. 9
    Асинхронность
    •  Производительность
    –  Код выше
    •  Интерфейс пользователя
    •  Проблемы
    –  Много лишнего шума
    –  Проблема синхронизации
    –  Куча вложенных колбэков: Pyramid of Doom
    •  Несколько реализаций
    –  Event Loop

    View Slide

  10. 10
    Event Loop
    •  Основа всех событийных систем
    •  Использует очередь событий
    •  Ждет события
    •  Выполняет события из очереди
    –  События в очередь поступают во время выполнения событий
    –  События генерируют события
    •  Завершается когда очередь пуста

    View Slide

  11. Паттерны
    Callback,
    Event,
    Promise,
    Deferred

    View Slide

  12. 12
    Типичный пример – обертка над XMLHttpRequest
    Callback
    function asyncXHR(method, url, data, callback) {
    var xhr = new XMLHttpRequest();
    xhr.open(method, url, true);
    xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
    if (xhr.status === 200) {
    callback(null, xhr.responseText);
    } else {
    callback(‘error’);
    }
    }
    }
    xhr.send(data);
    }

    View Slide

  13. 13
    Callback
    •  Самый простой вариант
    –  Дешевая абстракция
    •  В него могут приходить ошибки и данные
    –  cтиль node.js
    –  callback(err, data)

    View Slide

  14. 14
    Общая схема
    Event: EventEmitter, PubSub
    function EventEmitter () {
    this.events = {};
    }
    EventEmitter.prototype = {
    on: function (event, callback) {},
    off: function (event, callback) {},
    emit: function (event, data) {}
    };
    http://nodejs.org/api/events.html

    View Slide

  15. 15
    Типичный пример – обертка над XMLHttpRequest
    Event
    function eventXHR(method, url, data) {
    var xhr = new XMLHttpRequest(),
    event = new EventEmitter();
    xhr.open(method, url, true);
    xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
    if (xhr.status === 200) {
    event.emit(‘data’, xhr.responseText);
    } else {
    event.emit(‘error’);
    }
    }
    }
    xhr.send(data);
    return event;
    }

    View Slide

  16. 16
    Сам код. Изменилось не так много.
    Event
    eventXHR('GET', ‘http://host1/page.json’)
    .on(‘data’, function (data) {
    data = processData(data);
    var counter = 2;
    function done() {
    counter--;
    if (!counter) alert(‘Done!’);
    }
    eventXHR(‘POST’, ‘http://host2/result/’, data)
    .on(‘data’, done);
    eventXHR(‘POST’, ‘http://host3/result/’, data)
    .on(‘data’, done);
    })
    .on(‘error’, function(){ });

    View Slide

  17. 17
    Event
    •  Абстракция более высокого уровня
    •  Ошибки отделены от данных
    –  Возможны логически разные типы данных
    •  Можно отписаться от события
    •  Можно подписаться несколько раз
    •  Можно передавать как аргумент

    View Slide

  18. 18
    Promise
    •  Это Обещанные данные
    •  Имеет 3 состояния
    –  Не выполнен (выполняется)
    –  Выполнен (результат)
    –  Отклонен (ошибка)
    •  Меняет состояние только 1 раз
    –  В событиях состояние меняется сколько угодно раз
    •  Запоминает свое состояние
    –  В отличии от события в котором состояние – это поток
    http://wiki.commonjs.org/wiki/Promises

    View Slide

  19. 19
    Общая схема
    Promise
    function Promise () {
    this.isFulfilled = false;
    this.isRejected = false;
    this.isResolved = false;
    this.result = null;
    }
    Promise.prototype = {
    then: function (fulfilled, rejected, progressed) {},
    reject: function (error) {},
    resolve: function (data) {}
    };

    View Slide

  20. 20
    Типичный пример – обертка над XMLHttpRequest
    Promise
    function promiseXHR(method, url, data) {
    var xhr = new XMLHttpRequest(),
    promise = new Promise();
    xhr.open(method, url, true);
    xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
    if (xhr.status === 200) {
    promise.resolve(xhr.responseText);
    } else {
    promise.reject(‘Error ’ + xhr.status);
    }
    }
    }
    xhr.send(data);
    return promise;
    }

    View Slide

  21. 21
    Сам код
    Promise
    promiseXHR('GET', ‘http://host1/page.json’)
    .then(function (data) {
    data = processData(data);
    var promises = [
    promiseXHR(‘POST’, ‘http://host2/result/’, data),
    promiseXHR(‘POST’, ‘http://host3/result/’, data)
    ];
    when(promises, function (data) {
    alert(‘Done!’);
    });
    });

    View Slide

  22. 22
    Promise
    •  Запоминает свое состояние
    •  Всегда возвращает один результат
    –  В отличие от события где данные – поток
    –  Не зависит от времени опроса
    •  Можно передавать как аргумент
    •  Можно выполнять операции
    –  then

    View Slide

  23. 23
    Deferred
    •  Это защищенный Promise
    •  Разграничивает слушателя и Promise
    •  Слушатель не может вмешаться
    –  С чистыми промисами можно завершить промис на слушателе
    –  Меньше логических ошибок
    http://api.jquery.com/category/deferred-object/

    View Slide

  24. 24
    Общая схема
    Deferred
    function Deferred () {
    this._promise = {
    then: function (fulfilled, rejected, progressed) {}
    };
    }
    Deferred.prototype = {
    promise: function (error) {
    return this._promise;
    },
    reject: function (error) {},
    resolve: function (data) {}
    };

    View Slide

  25. 25
    Типичный пример – обертка над XMLHttpRequest
    Deferred
    function defferedXHR(method, url, data) {
    var xhr = new XMLHttpRequest(),
    deferred = new Deffered();
    xhr.open(method, url, true);
    xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
    if (xhr.status === 200) {
    deferred.resolve(xhr.responseText);
    } else {
    deferred.reject(‘Error ’ + xhr.status);
    }
    }
    }
    xhr.send(data);
    return deferred.promise();
    }

    View Slide

  26. 26
    Сам код
    Deferred
    defferedXHR('GET', ‘http://host1/page.json’)
    .then(function (data) {
    data = processData(data);
    var promises = [
    defferedXHR(‘POST’, ‘http://host2/result/’, data),
    defferedXHR(‘POST’, ‘http://host3/result/’, data)
    ];
    when(promises, function (data) {
    alert(‘Done!’);
    });
    })
    .reject(‘Mua-ha-ha!’); // Это сделать нельзя

    View Slide

  27. Библиотеки
    Streamlinejs,
    Fibers
    Step,
    Q

    View Slide

  28. 28
    Streamline – попытка избавится от асинхронного шума
    Используют callback(err, data)
    Streamline
    var data = asyncXHR('GET', '/', null, _);
    asyncXHR('POST', '/', data, _);
    asyncXHR('POST', '/', data, _);
    alert('Done!');
    https://github.com/Sage/streamlinejs

    View Slide

  29. 29
    Happy Debug!
    Streamline – результат генерации
    (function main(_) {
    var data;
    var __frame = {
    name: "main”,
    line: 1
    };
    return __func(_, this, arguments, main, 0, __frame, function __$main() {
    return asyncXHR("GET", "/", null, __cb(_, __frame, 17, 11, function ___(__0, __1) {
    data = __1;
    return asyncXHR("POST", "/", data, __cb(_, __frame, 18, 0, function __$main() {
    return asyncXHR("POST", "/", data, __cb(_, __frame, 19, 0, function __$main() {
    alert("Done!");
    _();
    }, true));
    }, true));
    }, true));
    });
    }).call(this, __trap);

    View Slide

  30. 30
    Streamlinejs
    •  Генерация кода – результат ужасен!
    •  Шум из массы _
    •  Его цель – выполнять асинхронный код
    последовательно

    View Slide

  31. 31
    Fibers – попытка избавится от асинхронного шума
    Используют callback(err, data)
    Fibers
    var Future = require('fibers/future'),
    wait = Future.wait;
    var asyncXHR = Future.wrap(asyncXHR);
    Fiber(function () {
    var data = asyncXHR(‘GET’, '...’, null).wait();
    data = processData(data);
    asyncXHR(‘POST’, '...’, data).wait();
    asyncXHR(‘POST’, '...’, data).wait();
    alert(‘Done!’);
    }).run();
    https://github.com/laverdet/node-fibers

    https://github.com/0ctave/node-sync

    View Slide

  32. 32
    Fibers
    •  Особая версия Node.js
    –  Хак механизма yield()
    •  Похожи на треды
    –  Не могут прерываться где угодно процессором
    –  Меньше расходов на «безопасные зоны»
    •  Похожи на Event Loop
    –  yield() и ручное прерывание фибера
    –  Блокировка остальных фиберов
    –  Нет реального параллелизма (не занимают все ядра процессора)
    •  Параллельные запросы последовательно
    –  Необходимо использовать дополнительные функции

    View Slide

  33. 33
    Позволяет выполнять асинхронный код в синхронном стиле
    Работает с callback(err, data)
    Step
    Step(
    function () {
    asyncXHR('GET’, ‘...’, null, this);
    },
    function (err, data) {
    return processData(data);
    },
    function (err, data) {
    asyncXHR(‘POST’, ‘...’, data, this.parallel());
    asyncXHR(‘POST’, ‘...’, data, this.parallel());
    },
    function (err, result1, result2) {
    alert(‘Done!’);
    }
    );
    https://github.com/creationix/step

    View Slide

  34. 34
    Работает с Promise
    Представляет интерфейс для работы с промисами
    Q
    var data = promiseXHR('GET', '...');
    data.than(processAndSendData).than(function () {
    alert(‘Done!’);
    });
    function processAndSendData(data) {
    data = processData(data);
    return sendData(data);
    }
    function sendData(data) {
    return Q.all([
    promiseXHR(‘POST’, ‘...’, data),
    promiseXHR(‘POST’, ‘...’, data)
    ]);
    }
    https://github.com/kriskowal/q

    View Slide

  35. 35
    Асинхронность
    •  Событийный ввод-вывод
    •  Работа с GUI
    •  Паттерны
    –  Callback
    –  EventEmitter
    –  Promise
    –  Deferred
    •  Хаки
    –  Streamline
    –  Fibers
    •  Библиотеки
    –  Step
    –  Q

    View Slide

  36. Михаил Давыдов
    Разработчик JavaScript
    [email protected]
    azproduction
    Спасибо

    View Slide