Михаил Давыдов: JavaScript. Асинхронность

63

description

 

Transcript of Михаил Давыдов: JavaScript. Асинхронность

Page 1: Михаил Давыдов: JavaScript. Асинхронность
Page 2: Михаил Давыдов: JavaScript. Асинхронность

Михаил Давыдов Разработчик JavaScript

JavaScript Асинхронность

Page 3: Михаил Давыдов: JavaScript. Асинхронность

3

Задача

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

Page 4: Михаил Давыдов: JavaScript. Асинхронность

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!');

Page 5: Михаил Давыдов: JavaScript. Асинхронность

5

Схема загрузки

время

Блокировка Блокировка Блокировка

Запрос Запрос Запрос

Подготовка Обработка Отправка Алерт

Page 6: Михаил Давыдов: JavaScript. Асинхронность

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); }

Page 7: Михаил Давыдов: JavaScript. Асинхронность

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); });

Page 8: Михаил Давыдов: JavaScript. Асинхронность

8

Схема загрузки

время

Ожидание Ожидание

Запрос

Подготовка Обработка Отправка Алерт

Page 9: Михаил Давыдов: JavaScript. Асинхронность

9

Где применяется "асинхронность"

• Производительность • Интерфейс пользователя • Проблемы

–  Много лишнего шума –  Проблема синхронизации –  Куча вложенных колбэков: Pyramid of Doom

• Несколько реализаций –  Event Loop

Page 10: Михаил Давыдов: JavaScript. Асинхронность

10

Event Loop

Page 11: Михаил Давыдов: JavaScript. Асинхронность

11

Event Loop

• Основа всех событийных систем • Использует очередь событий • Ждет события • Выполняет события из очереди

–  События в очередь поступают во время выполнения событий –  События генерируют события

• Завершается когда очередь пуста

Page 12: Михаил Давыдов: JavaScript. Асинхронность

12

Event Loop

var servers = [ 'http://serv1.ru/', 'http://serv2.ru/']; getFile('filename.jpg').then(function (file){ file = jpg2png(file); sendTo(file, servers).then(function (){ alert('tada!'); }); });

Список событий

Когда придет запрос к серверу – запусти этот код

Запрос к серверу

Page 13: Михаил Давыдов: JavaScript. Асинхронность

13

Event Loop

var servers = [ 'http://serv1.ru/', 'http://serv2.ru/']; getFile('filename.jpg').then(function (file){ file = jpg2png(file); sendTo(file, servers).then(function (){ alert('tada!'); }); });

Список событий

Пришел запрос к северу, выполняем обработчик Когда файл прочитается – запусти этот код

Файл прочитан

Page 14: Михаил Давыдов: JavaScript. Асинхронность

14

Event Loop

var servers = [ 'http://serv1.ru/', 'http://serv2.ru/']; getFile('filename.jpg').then(function (file){ file = jpg2png(file); sendTo(file, servers).then(function (){ alert('tada!'); }); });

Список событий

Файл прочитался, выполняем обработчик Когда файлы отправятся – запусти этот код

Файл отправлен

Файл отправлен

Page 15: Михаил Давыдов: JavaScript. Асинхронность

15

А что если будет несколько одновременных запросов?!

Page 16: Михаил Давыдов: JavaScript. Асинхронность

16

Фрейм 0 выполняем код программы

Запрос к серверу

Список событий

Старт программы + Сейчас выполняется

Page 17: Михаил Давыдов: JavaScript. Асинхронность

17

Фрейм N пришел Запрос 1

Запрос к серверу Запрос к серверу

Список событий Сейчас выполняется

Файл прочитан 1 +

Page 18: Михаил Давыдов: JavaScript. Асинхронность

18

Фрейм N+1 пришел Запрос 2

Запрос к серверу Запрос к серверу

Список событий Сейчас выполняется

Файл прочитан 1

Файл прочитан 2 +

Page 19: Михаил Давыдов: JavaScript. Асинхронность

19

Фрейм N+2 прочитался Файл 1

Запрос к серверу

Список событий Сейчас выполняется

Файл прочитан 1

Файл прочитан 2

+ Файл отправлен 1

Файл отправлен 1 +

Page 20: Михаил Давыдов: JavaScript. Асинхронность

20

Фрейм N+3 еще Запрос 3

Запрос к серверу

Список событий Сейчас выполняется

Файл прочитан 2

Файл отправлен 1

Файл отправлен 1

Запрос к серверу

Файл прочитан 3 +

Page 21: Михаил Давыдов: JavaScript. Асинхронность

21

Фрейм N+4 Файлы 1 отправили

Запрос к серверу

Список событий Сейчас выполняется

Файл прочитан 2

Файл прочитан 3

Файл отправлен 1

Файл отправлен 1

Затем

Page 22: Михаил Давыдов: JavaScript. Асинхронность

22

Фрейм N+5 Файлы 2 прочитали

Запрос к серверу

Список событий Сейчас выполняется

Файл прочитан 3

Файл прочитан 2

+ Файл отправлен 2

Файл отправлен 2 +

Page 23: Михаил Давыдов: JavaScript. Асинхронность

23

Фрейм N+6 Файлы 3 прочитали

Запрос к серверу

Список событий Сейчас выполняется

Файл прочитан 3

Файл отправлен 2

Файл отправлен 2

+ Файл отправлен 3

Файл отправлен 3 +

Page 24: Михаил Давыдов: JavaScript. Асинхронность

24

Фрейм N+7 Файлы 3 отправили

Запрос к серверу

Список событий Сейчас выполняется

Файл отправлен 3

Файл отправлен 3

Затем Файл отправлен 2

Файл отправлен 2

Page 25: Михаил Давыдов: JavaScript. Асинхронность

25

Фрейм N+8 Файлы 2 отправили

Запрос к серверу

Список событий Сейчас выполняется

Файл отправлен 2

Файл отправлен 2

Затем

Page 26: Михаил Давыдов: JavaScript. Асинхронность

26

Фрейм N+100500 убираем обработчик Список событий

Убрать событие

Сейчас выполняется

Page 27: Михаил Давыдов: JavaScript. Асинхронность

27

Когда очередь пуста – программа завершается

Page 28: Михаил Давыдов: JavaScript. Асинхронность

28

Таймеры в JavaScript

Page 29: Михаил Давыдов: JavaScript. Асинхронность

29

Таймеры это не sleep() – это события во времени, они используют Event Loop

Page 30: Михаил Давыдов: JavaScript. Асинхронность

30

Таймер без повтора

•  setTimeout(function, timeout): Number –  выполни эту функцию не раньше чем через это время –  таймаут - миллисекунды

•  clearTimeout(timerId) –  предотврати выполнение этого таймера –  ид таймера – обычное число

Page 31: Михаил Давыдов: JavaScript. Асинхронность

31

Таймер c повтором

•  setInterval(function, timeout): Number –  выполняй эту функцию через данный интервал –  интервал - миллисекунды

•  clearInterval(timerId) –  предотврати выполнение этого интрвала –  ид интервала – обычное число

Page 32: Михаил Давыдов: JavaScript. Асинхронность

32

Любой таймер будет вызван не раньше указанного времени

Page 33: Михаил Давыдов: JavaScript. Асинхронность

33

var time = new Date(); setTimeout(function () { console.log(new Date() - time); }, 1000); // Эта функция выполняется 1100 мсек thisFunctionTakes1100msec(); // 1102

Пример промаха таймера

Page 34: Михаил Давыдов: JavaScript. Асинхронность

34

JavaScript работает в одном потоке и не может прерывать обработчики

Page 35: Михаил Давыдов: JavaScript. Асинхронность

35

Что происходит

Получить текущее время

Подписаться на событие T+1000

Тяжелая функция (1100 мс)

Время

T+1000

Выполнение функции таймера

Запаздывание

Page 36: Михаил Давыдов: JavaScript. Асинхронность

36

Таймеры выполняются в том же потоке, что и программа

Page 37: Михаил Давыдов: JavaScript. Асинхронность

37

var time = new Date(); setTimeout(function () { console.log(new Date() - time); }, 1000); setTimeout(function () { // Эта функция выполняется 1100 мсек thisFunctionTakes1100msec(); }, 10); thisFunctionTakes1100msec(); // 2212 = 1100 + 10 + 1100 + x

Еще один пример промаха таймера

Page 38: Михаил Давыдов: JavaScript. Асинхронность

38

Таймер может никогда не выполниться

Page 39: Михаил Давыдов: JavaScript. Асинхронность

39

var time = new Date(); setTimeout(function () { console.log(new Date() - time); }, 1000); while(true);

Пример недостижимого таймера

Page 40: Михаил Давыдов: JavaScript. Асинхронность

Паттерны

Callback,

Event,

Promise,

Deferred

Page 41: Михаил Давыдов: JavaScript. Асинхронность

41

Типичный пример – обертка над 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); }

Page 42: Михаил Давыдов: JavaScript. Асинхронность

42

Callback

• Самый простой вариант –  Дешевая абстракция

• В него могут приходить ошибки и данные –  cтиль node.js –  callback(err, data)

Page 43: Михаил Давыдов: JavaScript. Асинхронность

43

Общая схема

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

Page 44: Михаил Давыдов: JavaScript. Асинхронность

44

Типичный пример – обертка над 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; }

Page 45: Михаил Давыдов: JavaScript. Асинхронность

45

Сам код. Изменилось не так много.

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(){ });

Page 46: Михаил Давыдов: JavaScript. Асинхронность

46

Event

• Абстракция более высокого уровня • Ошибки отделены от данных

–  Возможны логически разные типы данных

• Можно отписаться от события • Можно подписаться несколько раз • Можно передавать как аргумент

Page 47: Михаил Давыдов: JavaScript. Асинхронность

47

Promise

• Это Обещанные данные • Имеет 3 состояния

–  Не выполнен (выполняется) –  Выполнен (результат) –  Отклонен (ошибка)

• Меняет состояние только 1 раз –  В событиях состояние меняется сколько угодно раз

• Запоминает свое состояние –  В отличии от события в котором состояние – это поток

http://wiki.commonjs.org/wiki/Promises

Page 48: Михаил Давыдов: JavaScript. Асинхронность

48

Общая схема

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) {} };

Page 49: Михаил Давыдов: JavaScript. Асинхронность

49

Типичный пример – обертка над 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; }

Page 50: Михаил Давыдов: JavaScript. Асинхронность

50

Сам код

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) ]; return when(promises); }) .then(function (data) { alert('Done!'); })

Page 51: Михаил Давыдов: JavaScript. Асинхронность

51

Promise

• Запоминает свое состояние • Всегда возвращает один результат

–  В отличие от события где данные – поток –  Не зависит от времени опроса

• Можно передавать как аргумент • Можно выполнять операции

–  then

Page 52: Михаил Давыдов: JavaScript. Асинхронность

52

Deferred

• Это защищенный Promise • Разграничивает слушателя и Promise • Слушатель не может вмешаться

–  С чистыми промисами можно завершить промис на слушателе –  Меньше логических ошибок

http://api.jquery.com/category/deferred-object/

Page 53: Михаил Давыдов: JavaScript. Асинхронность

53

Общая схема

Deferred

function Deferred () { this._promise = { then: function (fulfilled, rejected, progressed) {} }; } Deferred.prototype = { promise: function (error) { return this._promise; }, reject: function (error) {}, resolve: function (data) {} };

Page 54: Михаил Давыдов: JavaScript. Асинхронность

54

Типичный пример – обертка над 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(); }

Page 55: Михаил Давыдов: JavaScript. Асинхронность

55

Сам код

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!’); // Это сделать нельзя

Page 56: Михаил Давыдов: JavaScript. Асинхронность

Библиотеки

Streamlinejs,

Step,

Q

Page 57: Михаил Давыдов: JavaScript. Асинхронность

57

Streamline – попытка избавится от асинхронного шума Используют callback(err, data)

Streamline

var data = asyncXHR('GET', '/', null, _); asyncXHR('POST', '/', data, _); asyncXHR('POST', '/', data, _); alert('Done!');

https://github.com/Sage/streamlinejs

Page 58: Михаил Давыдов: JavaScript. Асинхронность

58

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);

Page 59: Михаил Давыдов: JavaScript. Асинхронность

59

Streamlinejs

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

Page 60: Михаил Давыдов: JavaScript. Асинхронность

60

Позволяет выполнять асинхронный код в синхронном стиле Работает с 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

Page 61: Михаил Давыдов: JavaScript. Асинхронность

61

Работает с 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

Page 62: Михаил Давыдов: JavaScript. Асинхронность

62

Асинхронность

• Событийный ввод-вывод • Работа с GUI • Паттерны

–  Callback –  EventEmitter –  Promise –  Deferred

• Хаки –  Streamline –  Fibers

• Библиотеки –  Step –  Q

Page 63: Михаил Давыдов: JavaScript. Асинхронность

Михаил Давыдов

Разработчик JavaScript

[email protected] azproduction

Спасибо