16. 16
Идея событийного программирования
• Любое действие – событие
– Начало программы
– Клик на кнопку
– Событие во времени
– Конец чтения файла…
• Программа не ждет I/O
– Загрузка процесса предельно близка к 100%
• Подписывается на события I/O
• Выполняет код, когда событие наступило
24. 24
Профит
• Блокировка → Ожидание запроса
• Программа не блокируется
• Отправляет файлы параллельно
• 1 тред может обслуживать несколько
соединений
26. 26
Event Loop
• Один поток
• Использует системные команды
– *NIX: select, epoll, kqueue
– Win: GetMessage, PeekMessage
• Основа – список событий
• Подписываемся на событие
• Выполняем код, когда событие произошло
• Список событий пуст – конец
28. 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!');
});
});
Список событий
Когда придет запрос к серверу – запусти этот код
Запрос к серверу
29. 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!');
});
});
Список событий
Пришел запрос к северу, выполняем обработчик
Когда файл прочитается – запусти этот код
Файл прочитан
30. 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!');
});
});
Список событий
Файл прочитался, выполняем обработчик
Когда файлы отправятся – запусти этот код
Файл отправлен
Файл отправлен
31. 31
А что если будет несколько
одновременных запросов?!
32. 32
Фрейм 0 выполняем код программы
Запрос к серверу
Список событий
Старт программы +
Сейчас выполняется
33. 33
Фрейм N пришел Запрос 1
Запрос к серверу Запрос к серверу
Список событийСейчас выполняется
Файл прочитан 1+
34. 34
Фрейм N+1 пришел Запрос 2
Запрос к серверу Запрос к серверу
Список событийСейчас выполняется
Файл прочитан 1
Файл прочитан 2+
35. 35
Фрейм N+2 прочитался Файл 1
Запрос к серверу
Список событийСейчас выполняется
Файл прочитан 1
Файл прочитан 2
+
Файл отправлен 1
Файл отправлен 1+
36. 36
Фрейм N+3 еще Запрос 3
Запрос к серверу
Список событийСейчас выполняется
Файл прочитан 2
Файл отправлен 1
Файл отправлен 1
Запрос к серверу
Файл прочитан 3+
37. 37
Фрейм N+4 Файлы 1 отправили
Запрос к серверу
Список событийСейчас выполняется
Файл прочитан 2
Файл прочитан 3
Файл отправлен 1
Файл отправлен 1
Затем
38. 38
Фрейм N+5 Файлы 2 прочитали
Запрос к серверу
Список событийСейчас выполняется
Файл прочитан 3
Файл прочитан 2
+
Файл отправлен 2
Файл отправлен 2+
39. 39
Фрейм N+6 Файлы 3 прочитали
Запрос к серверу
Список событийСейчас выполняется
Файл прочитан 3
Файл отправлен 2
Файл отправлен 2
+
Файл отправлен 3
Файл отправлен 3+
40. 40
Фрейм N+7 Файлы 3 отправили
Запрос к серверу
Список событийСейчас выполняется
Файл отправлен 3
Файл отправлен 3
Затем
Файл отправлен 2
Файл отправлен 2
41. 41
Фрейм N+8 Файлы 2 отправили
Запрос к серверу
Список событийСейчас выполняется
Файл отправлен 2
Файл отправлен 2
Затем
45. 45
Таймеры это не sleep() –
это события во времени,
они используют Event Loop
46. 46
Таймер без повтора
• setTimeout(function, timeout): Number
– выполни эту функцию не раньше чем через это время
– таймаут - миллисекунды
• clearTimeout(timerId)
– предотврати выполнение этого таймера
– ид таймера – обычное число
48. 48
Таймер c повтором
• setInterval(function, timeout): Number
– выполняй эту функцию через данный интервал
– интервал - миллисекунды
• clearInterval(timerId)
– предотврати выполнение этого интрвала
– ид интервала – обычное число
49. 49
var times = 10;
var intervalId = setInterval(function () {
console.log(new Date());
times--;
if (!times) {
clearInterval(intervalId);
}
}, 1000);
Пример setInterval
51. 51
var time = new Date();
setTimeout(function () {
console.log(new Date() - time);
}, 1000);
// Эта функция выполняется 1100 мсек
thisFunctionTakes1100msec();
// 1102
Пример промаха таймера
53. 53
Что происходит
Получить текущее время
Подписаться на событие T+1000
Тяжелая функция (1100 мс)
Время
T+1000
Выполнение функции таймера
Запаздывание
55. 55
var time = new Date();
setTimeout(function () {
console.log(new Date() - time);
}, 1000);
setTimeout(function () {
// Эта функция выполняется 1100 мсек
thisFunctionTakes1100msec();
}, 10);
thisFunctionTakes1100msec();
// 2212 = 1100 + 10 + 1100 + x
Еще один пример промаха таймера
66. 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
67. 67
Статусы XMLHttpRequest
• UNSENT=0
– функция open() еще не вызвана
• OPENED=1
– функция send() еще не вызвана
• HEADERS_RECEIVED=2
– Пришли заголовки
• LOADING=3
– часть ответа пришла
• DONE=4
– запрос завершен
https://developer.mozilla.org/en-US/docs/DOM/
XMLHttpRequest
68. 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
69. 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);
}
70. 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);
}
71. 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);
}
72. 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);
}