3. Задача
• Качаем 1 файл
• После отправляем данные на 2 сервера
• Синхронизируемся
3
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!’);
4
9. Асинхронность
• Производительность
– Код выше
• Интерфейс пользователя
• Проблемы
– Много лишнего шума
– Проблема синхронизации
– Куча вложенных колбэков: Pyramid of Doom
• Несколько реализаций
– Event Loop
9
10. Event Loop
• Основа всех событийных систем
• Использует очередь событий
• Ждет события
• Выполняет события из очереди
– События в очередь поступают во время выполнения событий
– События генерируют события
• Завершается когда очередь пуста
10
12. Callback
Типичный пример – обертка над 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);
}
12
13. Callback
• Самый простой вариант
– Дешевая абстракция
• В него могут приходить ошибки и данные
– cтиль node.js
– callback(err, data)
13
14. Event: EventEmitter, PubSub
Общая схема
function EventEmitter () {
this.events = {};
}
EventEmitter.prototype = {
on: function (event, callback) {},
off: function (event, callback) {},
emit: function (event, data) {}
};
14
http://nodejs.org/api/events.html
15. Event
Типичный пример – обертка над XMLHttpRequest
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;
}
15
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(){ });
16
17. Event
• Абстракция более высокого уровня
• Ошибки отделены от данных
– Возможны логически разные типы данных
• Можно отписаться от события
• Можно подписаться несколько раз
• Можно передавать как аргумент
17
18. Promise
• Это Обещанные данные
• Имеет 3 состояния
– Не выполнен (выполняется)
– Выполнен (результат)
– Отклонен (ошибка)
• Меняет состояние только 1 раз
– В событиях состояние меняется сколько угодно раз
• Запоминает свое состояние
– В отличии от события в котором состояние – это поток
18
http://wiki.commonjs.org/wiki/Promises
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) {}
};
19
20. Promise
Типичный пример – обертка над XMLHttpRequest
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;
}
20
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!’);
});
});
21
22. Promise
• Запоминает свое состояние
• Всегда возвращает один результат
– В отличие от события где данные – поток
– Не зависит от времени опроса
• Можно передавать как аргумент
• Можно выполнять операции
– then
22
23. Deferred
• Это защищенный Promise
• Разграничивает слушателя и Promise
• Слушатель не может вмешаться
– С чистыми промисами можно завершить промис на слушателе
– Меньше логических ошибок
23
http://api.jquery.com/category/deferred-object/
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) {}
};
24
25. Deferred
Типичный пример – обертка над XMLHttpRequest
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();
}
25
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!’); // Это сделать нельзя
26
28. Streamline
Streamline – попытка избавится от асинхронного шума
Используют callback(err, data)
var data = asyncXHR('GET', '/', null, _);
asyncXHR('POST', '/', data, _);
asyncXHR('POST', '/', data, _);
alert('Done!');
28
https://github.com/Sage/streamlinejs
29. Streamline – результат генерации
Happy Debug!
(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);
29
30. Streamlinejs
• Генерация кода – результат ужасен!
• Шум из массы _
• Его цель – выполнять асинхронный код
последовательно
30
31. Fibers
Fibers – попытка избавится от асинхронного шума
Используют callback(err, data)
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
31 https://github.com/0ctave/node-sync
32. Fibers
• Особая версия Node.js
– Хак механизма yield()
• Похожи на треды
– Не могут прерываться где угодно процессором
– Меньше расходов на «безопасные зоны»
• Похожи на Event Loop
– yield() и ручное прерывание фибера
– Блокировка остальных фиберов
– Нет реального параллелизма (не занимают все ядра процессора)
• Параллельные запросы последовательно
– Необходимо использовать дополнительные функции
32
33. Step
Позволяет выполнять асинхронный код в синхронном стиле
Работает с callback(err, data)
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!’);
}
);
33
https://github.com/creationix/step
34. Q
Работает с Promise
Представляет интерфейс для работы с промисами
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)
]);
}
34
https://github.com/kriskowal/q