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

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

B827d6cfdfbcfce33700b0e6cc03e344?s=128

Mikhail Davydov

October 11, 2012
Tweet

Transcript

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

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

    на 2 сервера •  Синхронизируемся
  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!’);
  5. 5 Схема загрузки время Блокировка Блокировка Блокировка Запрос Запрос Запрос

    Подготовка Обработка Отправка Алерт
  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); }
  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); });
  8. 8 Схема загрузки время Ожидание Ожидание Запрос Подготовка Обработка Отправка

    Алерт
  9. 9 Асинхронность •  Производительность –  Код выше •  Интерфейс пользователя

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

    очередь событий •  Ждет события •  Выполняет события из очереди –  События в очередь поступают во время выполнения событий –  События генерируют события •  Завершается когда очередь пуста
  11. Паттерны Callback, Event, Promise, Deferred

  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); }
  13. 13 Callback •  Самый простой вариант –  Дешевая абстракция • 

    В него могут приходить ошибки и данные –  cтиль node.js –  callback(err, data)
  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
  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; }
  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(){ });
  17. 17 Event •  Абстракция более высокого уровня •  Ошибки отделены

    от данных –  Возможны логически разные типы данных •  Можно отписаться от события •  Можно подписаться несколько раз •  Можно передавать как аргумент
  18. 18 Promise •  Это Обещанные данные •  Имеет 3 состояния

    –  Не выполнен (выполняется) –  Выполнен (результат) –  Отклонен (ошибка) •  Меняет состояние только 1 раз –  В событиях состояние меняется сколько угодно раз •  Запоминает свое состояние –  В отличии от события в котором состояние – это поток http://wiki.commonjs.org/wiki/Promises
  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) {} };
  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; }
  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!’); }); });
  22. 22 Promise •  Запоминает свое состояние •  Всегда возвращает один

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

    Promise •  Слушатель не может вмешаться –  С чистыми промисами можно завершить промис на слушателе –  Меньше логических ошибок http://api.jquery.com/category/deferred-object/
  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) {} };
  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(); }
  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!’); // Это сделать нельзя
  27. Библиотеки Streamlinejs, Fibers Step, Q

  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
  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);
  30. 30 Streamlinejs •  Генерация кода – результат ужасен! •  Шум

    из массы _ •  Его цель – выполнять асинхронный код последовательно
  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
  32. 32 Fibers •  Особая версия Node.js –  Хак механизма yield()

    •  Похожи на треды –  Не могут прерываться где угодно процессором –  Меньше расходов на «безопасные зоны» •  Похожи на Event Loop –  yield() и ручное прерывание фибера –  Блокировка остальных фиберов –  Нет реального параллелизма (не занимают все ядра процессора) •  Параллельные запросы последовательно –  Необходимо использовать дополнительные функции
  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
  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
  35. 35 Асинхронность •  Событийный ввод-вывод •  Работа с GUI • 

    Паттерны –  Callback –  EventEmitter –  Promise –  Deferred •  Хаки –  Streamline –  Fibers •  Библиотеки –  Step –  Q
  36. Михаил Давыдов Разработчик JavaScript azproduction@yandex-team.ru azproduction Спасибо