Upgrade to Pro — share decks privately, control downloads, hide ads and more …

КРиПИ - JavaScript Асинхронность, таймеры, рабо...

КРиПИ - JavaScript Асинхронность, таймеры, работа с сервером

Mikhail Davydov

November 07, 2012
Tweet

More Decks by Mikhail Davydov

Other Decks in Education

Transcript

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

    отправляем данные на 2 сервера •  Вызываем alert()
  2. 4 Псевдокод программы var file = getFile('/filename.jpg'); file = jpg2png(file);

    sendFile(file, 'http://server1.ru/'); sendFile(file, 'http://server2.ru/'); alert('tada!');
  3. 5 1. Подготовка var file = getFile('/filename.jpg'); file = jpg2png(file);

    sendFile(file, 'http://server1.ru/'); sendFile(file, 'http://server2.ru/'); alert('tada!'); Старт TCP/IP сессии Отправка HTTP запроса Получение данных …
  4. 6 2. Обработка var file = getFile('/filename.jpg'); file = jpg2png(file);

    sendFile(file, 'http://server1.ru/'); sendFile(file, 'http://server2.ru/'); alert('tada!'); Переделываем JPG в PNG
  5. 7 3. Отправка var file = getFile('/filename.jpg'); file = jpg2png(file);

    sendFile(file, 'http://server1.ru/'); sendFile(file, 'http://server2.ru/'); alert('tada!'); Старт TCP/IP сессии Отправка HTTP запроса Получение данных …
  6. 8 4. Алерт var file = getFile('/filename.jpg'); file = jpg2png(file);

    sendFile(file, 'http://server1.ru/'); sendFile(file, 'http://server2.ru/'); alert('tada!'); Рисуем окно через системное API
  7. 9 Схема загрузки линейной программы время Блокировка Блокировка Блокировка Загрузка

    Отправка Отправка Подготовка Обработка Отправка Алерт
  8. 11 Стоимость операций I/O • L1-кэш 3 цикла • L2-кэш 14 циклов

    • RAM 250 циклов • Диск 41 000 000 циклов • Сеть 240 000 000
  9. 14

  10. 16 Идея событийного программирования •  Любое действие – событие – 

    Начало программы –  Клик на кнопку –  Событие во времени –  Конец чтения файла… •  Программа не ждет I/O –  Загрузка процесса предельно близка к 100% •  Подписывается на события I/O •  Выполняет код, когда событие наступило
  11. 18 Псевдокод событийной программы var servers = [ 'http://serv1.ru/', 'http://serv2.ru/'];

    getFile('filename.jpg').then(function (file){ file = jpg2png(file); sendTo(file, servers).then(function (){ alert('tada!'); }); });
  12. 19 1. Подготовка var servers = [ 'http://serv1.ru/', 'http://serv2.ru/']; getFile('filename.jpg').then(function

    (file){ file = jpg2png(file); sendTo(file, servers).then(function (){ alert('tada!'); }); }); Когда файл скачается вызови эту функцию
  13. 20 2. Обработка var servers = [ 'http://serv1.ru/', 'http://serv2.ru/']; getFile('filename.jpg').then(function

    (file){ file = jpg2png(file); sendTo(file, servers).then(function (){ alert('tada!'); }); }); Кодируем в PNG
  14. 21 3. Отправка var servers = [ 'http://serv1.ru/', 'http://serv2.ru/']; getFile('filename.jpg').then(function

    (file){ file = jpg2png(file); sendTo(file, servers).then(function (){ alert('tada!'); }); }); Когда файлы отправятся вызови эту функцию
  15. 22 4. Алерт var servers = [ 'http://serv1.ru/', 'http://serv2.ru/']; getFile('filename.jpg').then(function

    (file){ file = jpg2png(file); sendTo(file, servers).then(function (){ alert('tada!'); }); }); Рисуем системное окно
  16. 24 Профит •  Блокировка → Ожидание запроса •  Программа не

    блокируется •  Отправляет файлы параллельно •  1 тред может обслуживать несколько соединений
  17. 26 Event Loop •  Один поток •  Использует системные команды

    –  *NIX: select, epoll, kqueue –  Win: GetMessage, PeekMessage •  Основа – список событий •  Подписываемся на событие •  Выполняем код, когда событие произошло •  Список событий пуст – конец
  18. 28 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!'); }); }); Список событий Когда придет запрос к серверу – запусти этот код Запрос к серверу
  19. 29 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!'); }); }); Список событий Пришел запрос к северу, выполняем обработчик Когда файл прочитается – запусти этот код Файл прочитан
  20. 30 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!'); }); }); Список событий Файл прочитался, выполняем обработчик Когда файлы отправятся – запусти этот код Файл отправлен Файл отправлен
  21. 32 Фрейм 0 выполняем код программы Запрос к серверу Список

    событий Старт программы + Сейчас выполняется
  22. 33 Фрейм N пришел Запрос 1 Запрос к серверу Запрос

    к серверу Список событий Сейчас выполняется Файл прочитан 1 +
  23. 34 Фрейм N+1 пришел Запрос 2 Запрос к серверу Запрос

    к серверу Список событий Сейчас выполняется Файл прочитан 1 Файл прочитан 2 +
  24. 35 Фрейм N+2 прочитался Файл 1 Запрос к серверу Список

    событий Сейчас выполняется Файл прочитан 1 Файл прочитан 2 + Файл отправлен 1 Файл отправлен 1 +
  25. 36 Фрейм N+3 еще Запрос 3 Запрос к серверу Список

    событий Сейчас выполняется Файл прочитан 2 Файл отправлен 1 Файл отправлен 1 Запрос к серверу Файл прочитан 3 +
  26. 37 Фрейм N+4 Файлы 1 отправили Запрос к серверу Список

    событий Сейчас выполняется Файл прочитан 2 Файл прочитан 3 Файл отправлен 1 Файл отправлен 1 Затем
  27. 38 Фрейм N+5 Файлы 2 прочитали Запрос к серверу Список

    событий Сейчас выполняется Файл прочитан 3 Файл прочитан 2 + Файл отправлен 2 Файл отправлен 2 +
  28. 39 Фрейм N+6 Файлы 3 прочитали Запрос к серверу Список

    событий Сейчас выполняется Файл прочитан 3 Файл отправлен 2 Файл отправлен 2 + Файл отправлен 3 Файл отправлен 3 +
  29. 40 Фрейм N+7 Файлы 3 отправили Запрос к серверу Список

    событий Сейчас выполняется Файл отправлен 3 Файл отправлен 3 Затем Файл отправлен 2 Файл отправлен 2
  30. 41 Фрейм N+8 Файлы 2 отправили Запрос к серверу Список

    событий Сейчас выполняется Файл отправлен 2 Файл отправлен 2 Затем
  31. 46 Таймер без повтора •  setTimeout(function, timeout): Number –  выполни

    эту функцию не раньше чем через это время –  таймаут - миллисекунды •  clearTimeout(timerId) –  предотврати выполнение этого таймера –  ид таймера – обычное число
  32. 47 setTimeout(function () { console.log(1); }, 1000); var timerId =

    setTimeout(function () { console.log(2); }, 1000); console.log(3); clearTimeout(timerId); // 3, 1 Пример setTimeout
  33. 48 Таймер c повтором •  setInterval(function, timeout): Number –  выполняй

    эту функцию через данный интервал –  интервал - миллисекунды •  clearInterval(timerId) –  предотврати выполнение этого интрвала –  ид интервала – обычное число
  34. 49 var times = 10; var intervalId = setInterval(function ()

    { console.log(new Date()); times--; if (!times) { clearInterval(intervalId); } }, 1000); Пример setInterval
  35. 51 var time = new Date(); setTimeout(function () { console.log(new

    Date() - time); }, 1000); // Эта функция выполняется 1100 мсек thisFunctionTakes1100msec(); // 1102 Пример промаха таймера
  36. 53 Что происходит Получить текущее время Подписаться на событие T+1000

    Тяжелая функция (1100 мс) Время T+1000 Выполнение функции таймера Запаздывание
  37. 55 var time = new Date(); setTimeout(function () { console.log(new

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

    Date() - time); }, 1000); while(true); Пример не достижимого таймера
  39. 65 Возможности XMLHttpRequest •  Неблокирующие запросы –  GET, POST, PUT,

    DELETE, … –  Можно отправлять и блокирующие •  Нельзя отправлять на другой сервер –  В версии 2 можно
  40. 66 // GET запрос var xhr = new XMLHttpRequest(); //

    Подготавливаем запрос xhr.open('GET', 'http://server.ru/file.jpg', true); // Подписываемся на событие "изменение статуса" xhr.addEventListener('readystatechange', function () { // Когда ответ пришел if (xhr.readyState === 4) { // Печатаем тело ответа console.log(xhr.responseText); } }, false); // Отправляем запрос xhr.send(); Работа с XHR
  41. 67 Статусы XMLHttpRequest •  UNSENT=0 –  функция open() еще не

    вызвана •  OPENED=1 –  функция send() еще не вызвана •  HEADERS_RECEIVED=2 –  Пришли заголовки •  LOADING=3 –  часть ответа пришла •  DONE=4 –  запрос завершен https://developer.mozilla.org/en-US/docs/DOM/ XMLHttpRequest
  42. 68 Методы и свойства XHR •  open(method, url, isNotBlock) – 

    method – 'get', 'post', … –  url – 'http://pewpew.com', '/file.jpg', 'file.jpg', '//site.ru:8080/' •  send(body) –  body – post тело 'name=name&time=1345678&message=hello' •  readyState: Number •  responseText: String •  status: Number –  HTTP статус ответа – 200, 404, 500 •  addEventListener(event, function) •  ... https://developer.mozilla.org/en-US/docs/DOM/ XMLHttpRequest
  43. 69 Сделаем обертку над XMLHttpRequest Асинхронный XHR function asyncXHR(method, url,

    data, callback) { var xhr = new XMLHttpRequest(); xhr.open(method, url, true); xhr.addEventListener('readystatechange', function () { if (xhr.readyState === 4) { if (xhr.status === 200) { callback(null, xhr.responseText); } else { callback('error'); } } }); xhr.send(data); }
  44. 70 Когда статус изменится – вызови эту функцию Асинхронный XHR

    function asyncXHR(method, url, data, callback) { var xhr = new XMLHttpRequest(); xhr.open(method, url, true); xhr.addEventListener('readystatechange', function () { if (xhr.readyState === 4) { if (xhr.status === 200) { callback(null, xhr.responseText); } else { callback('error'); } } }); xhr.send(data); }
  45. 71 Если статус = "Готово" – проверяем статус ответа Асинхронный

    XHR function asyncXHR(method, url, data, callback) { var xhr = new XMLHttpRequest(); xhr.open(method, url, true); xhr.addEventListener('readystatechange', function () { if (xhr.readyState === 4) { if (xhr.status === 200) { callback(null, xhr.responseText); } else { callback('error'); } } }); xhr.send(data); }
  46. 72 Если статус ответа 200 (все хорошо) – вызываем функцию

    с данными Асинхронный XHR function asyncXHR(method, url, data, callback) { var xhr = new XMLHttpRequest(); xhr.open(method, url, true); xhr.addEventListener('readystatechange', function () { if (xhr.readyState === 4) { if (xhr.status === 200) { callback(null, xhr.responseText); } else { callback('error'); } } }); xhr.send(data); }
  47. 73 Используем asyncXHR('get', 'http://site.ru', null, function (err, data) { if

    (!err) { console.log(data); } }); Стало на много меньше кода
  48. 74 Перепишем наш абстрактный пример asyncXHR('get', 'filename.jpg', null, processThenSendFile); function

    processThenSendFile(err, file) { file = jpg2png(file); asyncXHR('post', '//site.ru/', file, alertWhenDone); } function alertWhenDone(err, status) { alert('tada'); }
  49. 75 Заключение •  Линейная программа –  треды –  форки – 

    потоки •  Событийная программа –  Любой I/O – событие •  Event Loop •  Таймеры •  Асинхронная работа с сервером –  AJAX –  XMLHttpRequest aka XHR