Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Как перестать отлаживать
асинхронный код
и начать жить
Андрей Саломатин
FrontendConf, Москва
21.05.2015
Schlecht!Script
Schlecht!Script
function f1*red(a, b) {…}
f1*red(a, b)
function f2*blue(c) {…}
f2*blue(c)
3
Функции имеют цвет
Schlecht!Script
/* OK! */
function outer*blue() {
inner*blue()
}
4
Синие могут вызывать только
другие синие функции
/* NOT...
Schlecht!Script
/* OK! */
function outer*red() {
inner2*red()
}
5
Красные могут вызывать и
красные и синие функции
/* OK! ...
Писать и вызывать красные
функции больно!
6
Schlecht!Script
7
Красные функции нужно
называть на немецком!
/* Интерпретатор не поймёт */
function authUser*red() {…}
fu...
Schlecht!Script
8
Красные функции нужно
называть на немецком!
/* RICHTIG! */
function benutzerAutorisierung!*rot() {…}
fun...
Как писать на Schlecht!Script?
9
JavaScript
JavaScript
extends
Schlecht!Script
11
Асинхронные функции
это боль
12
JavaScript
• По-другому работают if/else, for, return
• Нет try/catch
• Ломают абстракцию
13
Асинхронные функции это боль
// выполнять последовательно
for (var i = 0; i < 10; i++) {
if (shouldProcess(i)) {
results.push(process(i));
}
}
14
JavaS...
function maybeProcess(i) {
if (i >= 10) { return; }
shouldProcess(function(should) {
if (should) {
process(i, function(res...
JavaScript
• Не работают if/then, for и т.д.
• Нет try/catch
• Ломают абстракцию
16
Асинхронные функции это боль
Асинхронность
в JavaScript
17
Андрей Саломатин
Productive Mobile
MoscowJS
RadioJS
18
@filipovskii
Асинхронность
Множество событий
Единичная операция
20
Асинхронность: два сценария
Контроль
22
Исключения
23
Единый интерфейс
24
25
Контроль
Исключения
Единый интерфейс
• Работа с множеством событий

EventEmitter

Stream
• Работа с асинхронными операциями

Continuation Passing Style

Promis...
• Работа с множеством событий

Async Generators

• Работа с асинхронными операциями

Async/Await



28
ES7
ES6
Работа с множеством
асинхронных
событий
30
31
Работа с множеством асинхронных событий
EventEmitter
Stream
EventEmitter
32
Объект — источник событий
EventEmitter
33
Примеры
Browser: XMLHttpRequest
Node: http.Server
EventEmitter
emitter.addEventListener(eventName, cb);
emitter.removeEventListener(eventName, cb);
34
API
EventEmitter
35
События XMLHttpRequest
progress (n)
load (1)
abort (1)
error (1)
EventEmitter
36
Реализации
Браузер + Node:

EventEmitter3, Tiny Emitter
Node:

Node EventEmitter
37
Работа с множеством асинхронных событий
EventEmitter
Stream
38
EventEmitter
Stream
Работа с множеством асинхронных событий
Поток данных
Stream
39
Stream
40
Примеры
Node: fs.createReadStream(path)
Node: gulp.src(pattern)
Stream
41
Типы потоков
stylus files css files css prefixed files
gulp.src('*.styl') stylus() autoprefixer() gulp.dest('dis...
Stream
42
источник преобразование преобразование потребитель
gulp.src('*.styl') stylus() autoprefixer() gulp.dest('dist')
...
43
Stream
44
Реализации
Изоморфные:

RxJS, Kefir, Bacon.js
Node:

Node Streams
45
Работа с множеством
EventEmitter
Stream
46
Контроль
Исключения
Единый интерфейс
EventEmitter, Stream
Работа с
асинхронными операциями
47
48
Работа с асинхронными операциями
Continuation Passing Style
Promises
Coroutines
Continuation Passing Style
49
Примеры
Browser:
navigator.geolocation.getCurrentPosition(cb)
Node:
fs.stat(path, cb)
try {
var user = fetchUser(userId);
var following = fetchFollowingUsers(userId);
var tweets = fetchTweets(following);
hand...
fetchUser(userId, function(err, user) {
if (err) { return handleError(err); }
fetchFollowingUsers(user, function(err, foll...
fetchUser(userId, function(err, user) {
if (err) { return handleError(err); }
fetchFollowingUsers(user, function(err, foll...
holenBenutzer!*rot(userId, function(err, user) {
if (err) { return handleError*blue(err); }
holenFolgendenBenutzer!*rot(us...
54
Schlecht!Script
55
Работа с асинхронными операциями
Continuation Passing Style
Promises
Coroutines
56
Continuation Passing Style
Promises
Coroutines
Работа с асинхронными операциями
Объект — асинхронная
операция
Promises
57
Promise.prototype.then(successCb, errorCb);
58
API
Promises
fetchUser(userId)
.then(fetchFollowingUsers)
.then(fetchTweets)
.then(handleResult, handleError);
59
Promises
API
Promises
vs
Continuation Passing Style
60
61
Реализации
jQuery.Deffered
Bluebird
RSVP
Q
Promises
62
Continuation Passing Style
Promises
Coroutines
Работа с асинхронными операциями
63
Continuation Passing Style
Promises
Coroutines
Работа с асинхронными операциями
Coroutines
64
Coroutines
function getUserName(userId) {
var user = getUser(userId);
return user.name;
}
65
Остановите землю!
Функция, которую можно
приостановить и возобновить
позже
66
Coroutines
function * getUserName (userId) {
var user = yield getUser(userId);
return user.name;
};
67
Генераторы: шаг 1 из 3
Corouti...
function * getUserName (userId) {
var user = yield getUser(userId);
return user.name;
};
68
Coroutines
Генераторы: шаг 1 и...
getUserName = co.wrap(function * (userId) {
var user = yield getUser(userId);
return user.name;
});
69
Coroutines
Генерато...
getUserName = co.wrap(function * (userId) {
var user = yield getUser(userId);
return user.name;
});
70
Coroutines
Генерато...
userNamePromise = getUserName(userId);
71
Coroutines
Генераторы: шаг 3 из 3
Использовать генераторы для
асинхронного кода —
это хак
72
Использовать генераторы для
асинхронного кода —
это хак
73
(но я вас не выдам)
74
Реализации
Браузер + Node (используют транспайлер):

co (generators), task.js (generators)
Node (использует транспайлер...
75
Continuation Passing Style
Promises
Coroutines
Работа с асинхронными операциями
76
Контроль
Исключения
Единый интерфейс
CPS, Promises, Coroutines
Работа с множеством событий:
EventEmitter
Stream
Работа с асинхронными операциями:
CPS
Promise
Coroutine
77
ES6
ES7
Работа с асинхронными операциями:
Async/Await
Работа с множеством событий:
Async Generators
80
ES7
Работа с асинхронными операциями:
Async/Await
Работа с множеством событий:
Async Generators
81
ES7ES7
Async/Await
getUserName = co.wrap(function * (userId) {
var user = yield getUser(userId);
return user.name;
});
82
Генерат...
async function getUserName(userId) {
var user = await getUser(userId);
return user.name;
}
83
Async/Await
Async/Await
async function getUserName(userId) {
var user = await getUser(userId);
return user.name;
}
84
Асинхронная функция
Async/Aw...
Асинхронные функции
легализованы в ES7
85
Async/Await
Работа с асинхронными операциями:
Async/Await
Работа с множеством событий:
Async Generators
86
ES7
Работа с асинхронными операциями:
Async/Await
Работа с множеством событий:
Async Generators
87
ES7
Подписка на события
для людей
Async Generators
88
async function doDraw() {
for (let ev on observe(win, 'mousemove')) {
draw(ev.clientX, ev.clientY);
}
}
89
События DOM
Asy...
async function doDraw() {
for (let ev on observe(win, 'mousemove')) {
draw(ev.clientX, ev.clientY);
}
}
90
Async Generator...
doDrawPromise = doDraw();
91
Async Generators
События DOM
async function *filterWSMessages(ws) {
for (let msg on observe(ws, 'message')) {
if (isValid(msg)) yield msg;
}
}
92
WebSo...
async function *filterWSMessages(ws) {
for (let msg on observe(ws, 'message')) {
if (isValid(msg)) yield msg;
}
}
93
WebSo...
??? = filterWSMessages(ws);
??? = observe(win, 'mousemove');
??? = observe(ws, 'message');
94
Что возвращает Async Generat...
messagesObservable = filterWSMessages(ws);
eventsObservable = observe(win, 'mousemove');
eventsObservable = observe(ws, 'm...
Observables
Streams
aka
Работа с асинхронными операциями:
Async/Await
Работа с множеством событий:
Async Generators
97
ES7
ES6 и ES7:
асинхронные операции
98
fetchUser(userId, function(err, user) {
if (err) { return handleError(err); }
fetchFollowingUsers(user, function(err, foll...
fetchUser(userId)
.then(fetchFollowingUsers)
.then(fetchTweets)
.then(handleResult, handleError);
100
Получить ленту твито...
try {
var user = await fetchUser(userId);
var following = await fetchFollowingUsers(userId);
var tweets = await fetchTweet...
ES6 и ES7:
обработка множества событий
102
var handler = function(ev) {
if (canDraw(ev)) {
draw(ev.clientX, ev.clientY);
}
};
window.addEventListener('mousemove', ha...
Kefir.fromEvent(window, 'mousemove')
.filter(canDraw)
.onValue(function(ev) {
draw(ev.clientX, ev.clientY)
})
.end(); // п...
for (ev on observe(window, 'mousemove')) {
if (canDraw(ev)) {
draw(ev.clientX, ev.clientY);
}
}
105
DOM события: Async Gen...
Как перестать отлаживать
асинхронный
код и начать жить
106
Определите задачу
107
Обдумайте ограничения
108
Используйте лучшие практики
109
Schlecht!Script?
Спасибо!
bit.ly/async-js
Андрей Саломатин
@filipovskii
FrontendConf, Москва
21.05.2015
Как перестать отлаживать асинхронный код и начать жить / Андрей Саломатин (Productive Mobile)
Как перестать отлаживать асинхронный код и начать жить / Андрей Саломатин (Productive Mobile)
Как перестать отлаживать асинхронный код и начать жить / Андрей Саломатин (Productive Mobile)
Upcoming SlideShare
Loading in …5
×

Как перестать отлаживать асинхронный код и начать жить / Андрей Саломатин (Productive Mobile)

907 views

Published on

Асинхронность в Javascript больше не страшна. Классические триллеры вроде "Callback Hell" и "Pyramid of Doom" потеряли свою актуальность настолько, что даже Java-программисты перестали пугать ими невинных джуниоров.

Всё благодаря паттернам и библиотекам. Streams, Promises, Async-Await и другие изменили наш код. Теперь он прекрасен.

Пока ещё вымысел? Поговорим о том, как сделать эту картину реальностью. Об основных практиках асинхронного программирования, принципах их работы, отличиях и сценариях использования.

Published in: Engineering
  • Be the first to comment

  • Be the first to like this

Как перестать отлаживать асинхронный код и начать жить / Андрей Саломатин (Productive Mobile)

  1. 1. Как перестать отлаживать асинхронный код и начать жить Андрей Саломатин FrontendConf, Москва 21.05.2015
  2. 2. Schlecht!Script
  3. 3. Schlecht!Script function f1*red(a, b) {…} f1*red(a, b) function f2*blue(c) {…} f2*blue(c) 3 Функции имеют цвет
  4. 4. Schlecht!Script /* OK! */ function outer*blue() { inner*blue() } 4 Синие могут вызывать только другие синие функции /* NOT OK! */ function outer*blue() { inner*red() }
  5. 5. Schlecht!Script /* OK! */ function outer*red() { inner2*red() } 5 Красные могут вызывать и красные и синие функции /* OK! */ function outer*red() { inner2*blue() }
  6. 6. Писать и вызывать красные функции больно! 6
  7. 7. Schlecht!Script 7 Красные функции нужно называть на немецком! /* Интерпретатор не поймёт */ function authUser*red() {…} function getName*red() {…}
  8. 8. Schlecht!Script 8 Красные функции нужно называть на немецком! /* RICHTIG! */ function benutzerAutorisierung!*rot() {…} function nameErhalten!*rot() {…}
  9. 9. Как писать на Schlecht!Script? 9
  10. 10. JavaScript
  11. 11. JavaScript extends Schlecht!Script 11
  12. 12. Асинхронные функции это боль 12
  13. 13. JavaScript • По-другому работают if/else, for, return • Нет try/catch • Ломают абстракцию 13 Асинхронные функции это боль
  14. 14. // выполнять последовательно for (var i = 0; i < 10; i++) { if (shouldProcess(i)) { results.push(process(i)); } } 14 JavaScript if/for синхронно
  15. 15. function maybeProcess(i) { if (i >= 10) { return; } shouldProcess(function(should) { if (should) { process(i, function(result) { results.push(result); maybeProcess(i++); }); } maybeProcess(i++); }); } maybeProcess(0); 15 JavaScript if/for асинхронно
  16. 16. JavaScript • Не работают if/then, for и т.д. • Нет try/catch • Ломают абстракцию 16 Асинхронные функции это боль
  17. 17. Асинхронность в JavaScript 17
  18. 18. Андрей Саломатин Productive Mobile MoscowJS RadioJS 18 @filipovskii
  19. 19. Асинхронность
  20. 20. Множество событий Единичная операция 20 Асинхронность: два сценария
  21. 21. Контроль 22
  22. 22. Исключения 23
  23. 23. Единый интерфейс 24
  24. 24. 25 Контроль Исключения Единый интерфейс
  25. 25. • Работа с множеством событий
 EventEmitter
 Stream • Работа с асинхронными операциями
 Continuation Passing Style
 Promises
 Coroutines 27 ES6
  26. 26. • Работа с множеством событий
 Async Generators
 • Работа с асинхронными операциями
 Async/Await
 
 28 ES7
  27. 27. ES6
  28. 28. Работа с множеством асинхронных событий 30
  29. 29. 31 Работа с множеством асинхронных событий EventEmitter Stream
  30. 30. EventEmitter 32 Объект — источник событий
  31. 31. EventEmitter 33 Примеры Browser: XMLHttpRequest Node: http.Server
  32. 32. EventEmitter emitter.addEventListener(eventName, cb); emitter.removeEventListener(eventName, cb); 34 API
  33. 33. EventEmitter 35 События XMLHttpRequest progress (n) load (1) abort (1) error (1)
  34. 34. EventEmitter 36 Реализации Браузер + Node:
 EventEmitter3, Tiny Emitter Node:
 Node EventEmitter
  35. 35. 37 Работа с множеством асинхронных событий EventEmitter Stream
  36. 36. 38 EventEmitter Stream Работа с множеством асинхронных событий
  37. 37. Поток данных Stream 39
  38. 38. Stream 40 Примеры Node: fs.createReadStream(path) Node: gulp.src(pattern)
  39. 39. Stream 41 Типы потоков stylus files css files css prefixed files gulp.src('*.styl') stylus() autoprefixer() gulp.dest('dist')
  40. 40. Stream 42 источник преобразование преобразование потребитель gulp.src('*.styl') stylus() autoprefixer() gulp.dest('dist') Типы потоков
  41. 41. 43
  42. 42. Stream 44 Реализации Изоморфные:
 RxJS, Kefir, Bacon.js Node:
 Node Streams
  43. 43. 45 Работа с множеством EventEmitter Stream
  44. 44. 46 Контроль Исключения Единый интерфейс EventEmitter, Stream
  45. 45. Работа с асинхронными операциями 47
  46. 46. 48 Работа с асинхронными операциями Continuation Passing Style Promises Coroutines
  47. 47. Continuation Passing Style 49 Примеры Browser: navigator.geolocation.getCurrentPosition(cb) Node: fs.stat(path, cb)
  48. 48. try { var user = fetchUser(userId); var following = fetchFollowingUsers(userId); var tweets = fetchTweets(following); handleResult(tweets); } catch (err) { handleError(err); } 50 Continuation Passing Style Получить ленту твитов синхронно Получить ленту твитов асинхронно
  49. 49. fetchUser(userId, function(err, user) { if (err) { return handleError(err); } fetchFollowingUsers(user, function(err, following) { if (err) { return handleError(err); } fetchTweets(following, function(err, tweets) { if (err) { return handleError(err); } handleResult(tweets); }); }); }); 51 Continuation Passing Style Получить ленту твитов асинхронно
  50. 50. fetchUser(userId, function(err, user) { if (err) { return handleError(err); } fetchFollowingUsers(user, function(err, following) { if (err) { return handleError(err); } fetchTweets(following, function(err, tweets) { if (err) { return handleError(err); } 52 Continuation Passing Style Получить ленту твитов асинхронно
  51. 51. holenBenutzer!*rot(userId, function(err, user) { if (err) { return handleError*blue(err); } holenFolgendenBenutzer!*rot(user, function(err, following) { if (err) { return handleError*blue(err); } holenTweets!*rot(following, function(err, tweets) { if (err) { return handleError(err); } 53 Continuation Passing Style Holen Sie sich die Band tweets asynchron!
  52. 52. 54 Schlecht!Script
  53. 53. 55 Работа с асинхронными операциями Continuation Passing Style Promises Coroutines
  54. 54. 56 Continuation Passing Style Promises Coroutines Работа с асинхронными операциями
  55. 55. Объект — асинхронная операция Promises 57
  56. 56. Promise.prototype.then(successCb, errorCb); 58 API Promises
  57. 57. fetchUser(userId) .then(fetchFollowingUsers) .then(fetchTweets) .then(handleResult, handleError); 59 Promises API
  58. 58. Promises vs Continuation Passing Style 60
  59. 59. 61 Реализации jQuery.Deffered Bluebird RSVP Q Promises
  60. 60. 62 Continuation Passing Style Promises Coroutines Работа с асинхронными операциями
  61. 61. 63 Continuation Passing Style Promises Coroutines Работа с асинхронными операциями
  62. 62. Coroutines 64
  63. 63. Coroutines function getUserName(userId) { var user = getUser(userId); return user.name; } 65 Остановите землю!
  64. 64. Функция, которую можно приостановить и возобновить позже 66 Coroutines
  65. 65. function * getUserName (userId) { var user = yield getUser(userId); return user.name; }; 67 Генераторы: шаг 1 из 3 Coroutines
  66. 66. function * getUserName (userId) { var user = yield getUser(userId); return user.name; }; 68 Coroutines Генераторы: шаг 1 из 3
  67. 67. getUserName = co.wrap(function * (userId) { var user = yield getUser(userId); return user.name; }); 69 Coroutines Генераторы: шаг 2 из 3
  68. 68. getUserName = co.wrap(function * (userId) { var user = yield getUser(userId); return user.name; }); 70 Coroutines Генераторы: шаг 2 из 3
  69. 69. userNamePromise = getUserName(userId); 71 Coroutines Генераторы: шаг 3 из 3
  70. 70. Использовать генераторы для асинхронного кода — это хак 72
  71. 71. Использовать генераторы для асинхронного кода — это хак 73 (но я вас не выдам)
  72. 72. 74 Реализации Браузер + Node (используют транспайлер):
 co (generators), task.js (generators) Node (использует транспайлер):
 Fibers Coroutines
  73. 73. 75 Continuation Passing Style Promises Coroutines Работа с асинхронными операциями
  74. 74. 76 Контроль Исключения Единый интерфейс CPS, Promises, Coroutines
  75. 75. Работа с множеством событий: EventEmitter Stream Работа с асинхронными операциями: CPS Promise Coroutine 77 ES6
  76. 76. ES7
  77. 77. Работа с асинхронными операциями: Async/Await Работа с множеством событий: Async Generators 80 ES7
  78. 78. Работа с асинхронными операциями: Async/Await Работа с множеством событий: Async Generators 81 ES7ES7
  79. 79. Async/Await getUserName = co.wrap(function * (userId) { var user = yield getUser(userId); return user.name; }); 82 Генераторы
  80. 80. async function getUserName(userId) { var user = await getUser(userId); return user.name; } 83 Async/Await Async/Await
  81. 81. async function getUserName(userId) { var user = await getUser(userId); return user.name; } 84 Асинхронная функция Async/Await
  82. 82. Асинхронные функции легализованы в ES7 85 Async/Await
  83. 83. Работа с асинхронными операциями: Async/Await Работа с множеством событий: Async Generators 86 ES7
  84. 84. Работа с асинхронными операциями: Async/Await Работа с множеством событий: Async Generators 87 ES7
  85. 85. Подписка на события для людей Async Generators 88
  86. 86. async function doDraw() { for (let ev on observe(win, 'mousemove')) { draw(ev.clientX, ev.clientY); } } 89 События DOM Async Generators
  87. 87. async function doDraw() { for (let ev on observe(win, 'mousemove')) { draw(ev.clientX, ev.clientY); } } 90 Async Generators События DOM
  88. 88. doDrawPromise = doDraw(); 91 Async Generators События DOM
  89. 89. async function *filterWSMessages(ws) { for (let msg on observe(ws, 'message')) { if (isValid(msg)) yield msg; } } 92 WebSocket сообщения Async Generators
  90. 90. async function *filterWSMessages(ws) { for (let msg on observe(ws, 'message')) { if (isValid(msg)) yield msg; } } 93 WebSocket сообщения Async Generators
  91. 91. ??? = filterWSMessages(ws); ??? = observe(win, 'mousemove'); ??? = observe(ws, 'message'); 94 Что возвращает Async Generator? Async Generators
  92. 92. messagesObservable = filterWSMessages(ws); eventsObservable = observe(win, 'mousemove'); eventsObservable = observe(ws, 'message'); 95 Async Generators Что возвращает Async Generator?
  93. 93. Observables Streams aka
  94. 94. Работа с асинхронными операциями: Async/Await Работа с множеством событий: Async Generators 97 ES7
  95. 95. ES6 и ES7: асинхронные операции 98
  96. 96. fetchUser(userId, function(err, user) { if (err) { return handleError(err); } fetchFollowingUsers(user, function(err, following) { if (err) { return handleError(err); } fetchTweets(following, function(err, tweets) { if (err) { return handleError(err); } handleResult(tweets); }); }); }); 99 ES6 и ES7 Получить ленту твитов: CPS
  97. 97. fetchUser(userId) .then(fetchFollowingUsers) .then(fetchTweets) .then(handleResult, handleError); 100 Получить ленту твитов: Promise ES6 и ES7
  98. 98. try { var user = await fetchUser(userId); var following = await fetchFollowingUsers(userId); var tweets = await fetchTweets(following); handleResult(tweets); } catch (err) { handleError(err); } 101 Получить ленту твитов: Async/Await ES6 и ES7
  99. 99. ES6 и ES7: обработка множества событий 102
  100. 100. var handler = function(ev) { if (canDraw(ev)) { draw(ev.clientX, ev.clientY); } }; window.addEventListener('mousemove', handler); window.removeEventListener('mousemove', handler); // позже 103 DOM события: EventEmitter ES6 и ES7
  101. 101. Kefir.fromEvent(window, 'mousemove') .filter(canDraw) .onValue(function(ev) { draw(ev.clientX, ev.clientY) }) .end(); // позже 104 DOM события: Stream ES6 и ES7
  102. 102. for (ev on observe(window, 'mousemove')) { if (canDraw(ev)) { draw(ev.clientX, ev.clientY); } } 105 DOM события: Async Generators ES6 и ES7
  103. 103. Как перестать отлаживать асинхронный код и начать жить 106
  104. 104. Определите задачу 107
  105. 105. Обдумайте ограничения 108
  106. 106. Используйте лучшие практики 109
  107. 107. Schlecht!Script?
  108. 108. Спасибо! bit.ly/async-js Андрей Саломатин @filipovskii FrontendConf, Москва 21.05.2015

×