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!’);
9. 9
Асинхронность
• Производительность
– Код выше
• Интерфейс пользователя
• Проблемы
– Много лишнего шума
– Проблема синхронизации
– Куча вложенных колбэков: Pyramid of Doom
• Несколько реализаций
– Event Loop
10. 10
Event Loop
• Основа всех событийных систем
• Использует очередь событий
• Ждет события
• Выполняет события из очереди
– События в очередь поступают во время выполнения событий
– События генерируют события
• Завершается когда очередь пуста
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;
}
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!’); // Это сделать нельзя
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