Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
DevNexus 2017: The Rise of Async JavaScript
Search
Jeremy Fairbank
February 24, 2017
Programming
15
1.2k
DevNexus 2017: The Rise of Async JavaScript
Jeremy Fairbank
February 24, 2017
Tweet
Share
More Decks by Jeremy Fairbank
See All by Jeremy Fairbank
Connect.Tech 2020: Advanced Cypress Testing
jfairbank
1
190
CodeMash 2020: Solving the Boolean Identity Crisis
jfairbank
1
140
CodeMash 2020: Practical Functional Programming
jfairbank
1
310
Connect.Tech 2019: Practical Functional Programming
jfairbank
0
330
Connect.Tech 2019: Solving the Boolean Identity Crisis
jfairbank
0
170
Lambda Squared 2019: Solving the Boolean Identity Crisis
jfairbank
0
110
All Things Open 2018: Practical Functional Programming
jfairbank
2
250
Connect.Tech 2018: Effective React Testing
jfairbank
1
140
Fluent Conf 2018: Building web apps with Elm Tutorial
jfairbank
2
820
Other Decks in Programming
See All in Programming
複数のAWSアカウントから横断で 利用する Lambda Authorizer の作り方
tc3jp
0
120
たのしいSocketのしくみ / Socket Under a Microscope
coe401_
8
1.4k
ナレッジイネイブリングにAIを活用してみる ゆるSRE勉強会 #9
nealle
0
160
CloudNativePGを布教したい
nnaka2992
0
120
データの整合性を保つ非同期処理アーキテクチャパターン / Async Architecture Patterns
mokuo
55
19k
PEPCは何を変えようとしていたのか
ken7253
3
300
機能が複雑化しても 頼りになる FactoryBotの話
tamikof
1
230
Amazon Bedrockマルチエージェントコラボレーションを諦めてLangGraphに入門してみた
akihisaikeda
1
150
「個人開発マネタイズ大全」が教えてくれたこと
bani24884
1
280
ファインディLT_ポケモン対戦の定量的分析
fufufukakaka
0
940
推しメソッドsource_locationのしくみを探る - はじめてRubyのコードを読んでみた
nobu09
2
340
AIプログラミング雑キャッチアップ
yuheinakasaka
19
5k
Featured
See All Featured
Embracing the Ebb and Flow
colly
84
4.6k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
193
16k
Statistics for Hackers
jakevdp
797
220k
[RailsConf 2023] Rails as a piece of cake
palkan
53
5.3k
Why Our Code Smells
bkeepers
PRO
336
57k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
6
580
XXLCSS - How to scale CSS and keep your sanity
sugarenia
248
1.3M
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
231
53k
Producing Creativity
orderedlist
PRO
344
40k
Making the Leap to Tech Lead
cromwellryan
133
9.1k
Optimising Largest Contentful Paint
csswizardry
34
3.1k
Transcript
the RISEof async JavaScript Jeremy Fairbank blog.jeremyfairbank.com @elpapapollo | gh/jfairbank
sigient.com
None
None
None
None
“Call me maybe?”
Async API Callback
Callback fetchUserById(1, function(err, user) { if (err) { console.error('Could not
retrieve user'); } else { console.log(user); } });
}); }); }); }); }); }); Callback Callback Callback Callback
Callback Callback
function fetchCustomerNameForOrder(orderId, done, fail) { fetchOrder(orderId, function(err, order) { if
(err) { logError(err); fail(err); } else { fetchCustomer( order.customerId, function(err, customer) { if (err) { logError(err); fail(err); } else { done(customer.name); } } ); } }); }
function fetchCustomerNameForOrder(orderId, done, fail) { fetchOrder(orderId, function(err, order) { if
(err) { logError(err); fail(err); } else { fetchCustomer( order.customerId, function(err, customer) { if (err) { logError(err); fail(err); } else { done(customer.name); } } ); } }); }
function fetchCustomerNameForOrder(orderId, done, fail) { fetchOrder(orderId, function(err, order) { if
(err) { logError(err); fail(err); } else { fetchCustomer( order.customerId, function(err, customer) { if (err) { logError(err); fail(err); } else { done(customer.name); } } ); } }); }
Async API ?
Promise function fetchCustomerNameForOrder(orderId) { return fetchOrder(orderId) .then(order => fetchCustomer(order.customerId)) .then(customer
=> customer.name); }
.then(...) .then(...) .then(...) .then(...) .then(...) .then(...) .then(...) .then(...)
Error Error Error Error
function fetchCustomerNameForOrder(orderId) { return fetchOrder(orderId) .then(order => fetchCustomer(order.customerId)) .then(customer =>
customer.name) .catch(err => { logError(err); throw err; }); }
Getting there…
• More readable and maintainable • Synchronous-looking but nonblocking •
Use native flow control constructs • More declarative and versatile
async function fetchCustomerNameForOrder(orderId) { try { const order = await
fetchOrder(orderId); const customer = await fetchCustomer(order.customerId); return customer.name; } catch (err) { logError(err); throw err; } }
async function fetchCustomerNameForOrder(orderId) { try { const order = await
fetchOrder(orderId); const customer = await fetchCustomer(order.customerId); return customer.name; } catch (err) { logError(err); throw err; } }
async function fetchCustomerNameForOrder(orderId) { try { const order = await
fetchOrder(orderId); const customer = await fetchCustomer(order.customerId); return customer.name; } catch (err) { logError(err); throw err; } }
async function fetchCustomerNameForOrder(orderId) { try { const order = await
fetchOrder(orderId); const customer = await fetchCustomer(order.customerId); return customer.name; } catch (err) { logError(err); throw err; } }
Await
function fetchOrder(orderId) { return fetch(`/orders/${orderId}`) .then(response => response.json()); } function
printOrder(orderId) { fetchOrder(orderId) .then(order => console.log(order)); }
function fetchOrder(orderId) { return fetch(`/orders/${orderId}`) .then(response => response.json()); } async
function printOrder(orderId) { const order = await fetchOrder(orderId); console.log(order); }
async printOrder(orderId) { const order = await fetchOrder(orderId); console.log(order); }
fetchOrder(orderId) order
async printOrder(orderId) { const order = await fetchOrder(orderId); console.log(order); }
await
async printOrder(orderId) { const order = await fetchOrder(orderId); console.log(order); }
await order
await order
await order
async printOrder(orderId) { const order = await fetchOrder(orderId); console.log(order); }
?
async function printOrder(orderId) { const order = await fetchOrder(orderId); console.log(order);
} printOrder(1); Invoke
async function printOrder(orderId) { const order = await fetchOrder(orderId); console.log(order);
} printOrder(1); Encounter await
async function printOrder(orderId) { const order = await fetchOrder(orderId); console.log(order);
} printOrder(1); Wrap in Promise.resolve
Wrap in Promise.resolve async function printOrder(orderId) { const promise =
Promise.resolve(fetchOrder(orderId)); console.log(order); } printOrder(1);
async function printOrder(orderId) { const promise = Promise.resolve(fetchOrder(orderId)); console.log(order); }
printOrder(1); Wrap rest with then callback
async function printOrder(orderId) { const promise = Promise.resolve(fetchOrder(orderId)); return promise.then(order
=> { console.log(order); return Promise.resolve(undefined); }); } printOrder(1); Wrap rest with then callback
async function printOrder(orderId) { const promise = Promise.resolve(fetchOrder(orderId)); return promise.then(order
=> { console.log(order); return Promise.resolve(undefined); }); } printOrder(1); Wrap implicit return with Promise.resolve
function fetchCustomerNameForOrder(orderId) { return Promise.resolve(fetchOrder(orderId)) .then(order => { return Promise.resolve(fetchCustomer(order.customerId))
.then(customer => { return Promise.resolve(customer.name); }); }); } ?
async function meaningOfLife() { return 42; } meaningOfLife() === 42;
???
async function meaningOfLife() { return 42; } function meaningOfLife() {
return Promise.resolve(42); } meaningOfLife() .then(answer => answer === 42);
Exceptional Situations
async function printOrder(orderId) { try { const order = await
fetchOrder(orderId); console.log(order); } catch (e) { console.log('Error retrieving order', e); } }
async function printOrder(orderId) { try { const order = await
fetchOrder(orderId); console.log(order); } catch (e) { console.log('Error retrieving order', e); } } ?
function fetchOrder(orderId) { return fetch(`/orders/${orderId}`) .then(resp => { if (resp.status
=== 404) { throw new Error('Order not found'); } return resp.json(); }); }
function fetchOrder(orderId) { return new Promise((resolve, reject) => { $.get(`/orders/${orderId}`)
.done(order => resolve(order)) .fail(resp => { if (resp.status === 404) { reject(new Error('Order not found')); } }); }); }
Error Error Error Error
Regain Control
async function findOrCreateOrder(orderId) { let order; if (await orderExists(orderId)) {
order = await fetchOrder(orderId); } else { order = await createOrder(); } return order; }
async function findOrCreateOrder(orderId) { let order; if (await orderExists(orderId)) {
order = await fetchOrder(orderId); } else { order = await createOrder(); } return order; }
async function printOrders(orderIds) { for (const id of orderIds) {
const order = await fetchOrder(id); console.log(order); } }
async function printOrders(orderIds) { for (const id of orderIds) {
const order = await fetchOrder(id); console.log(order); } }
async function printOrders(orderIds) { for (const id of orderIds) {
const order = await fetchOrder(id); console.log(order); } } Problem?
async function printOrders(orderIds) { for (const id of orderIds) {
const order = await fetchOrder(id); console.log(order); } } printOrders([1, 2, 3]);
printOrders([1, 2, 3]); await fetchOrder(1); await fetchOrder(2); await fetchOrder(3);
printOrders([1, 2, 3]); await fetchOrder(1); await fetchOrder(2); await fetchOrder(3);
printOrders([1, 2, 3]); await fetchOrder(1); await fetchOrder(2); await fetchOrder(3);
Order is important
async function printOrders(orderIds) { const orders = await Promise.all( orderIds.map(fetchOrder)
); orders.forEach(order => console.log(order)); }
async function printOrders(orderIds) { const orders = await Promise.all( orderIds.map(fetchOrder)
); orders.forEach(order => console.log(order)); }
printOrders([1, 2, 3]); await Promise.all([ fetchOrder(1), fetchOrder(2), fetchOrder(3) ]);
printOrders([1, 2, 3]); Demo rise.of.async.jeremyfairbank.com
• Readable, synchronous- like code • Native flow control constructs
• Sequential and concurrent processing
• Readable, synchronous- like code • Native flow control constructs
• Sequential and concurrent processing ✓ ✓ ✓ ✓
Events and streams?
let counter = 0; function updateCounter(n) { counter += n;
counterEl.innerHTML = counter; } incrementBtn.addEventListener('click', () => { updateCounter(1); }); decrementBtn.addEventListener('click', () => { updateCounter(-1); });
let counter = 0; function updateCounter(n) { counter += n;
counterEl.innerHTML = counter; } incrementBtn.addEventListener('click', () => { updateCounter(1); }); decrementBtn.addEventListener('click', () => { updateCounter(-1); }); ×
let counter = 0; async function listenIncrement() { await click(incrementBtn);
counter += 1; counterEl.innerHTML = counter; listenIncrement(); }
let counter = 0; async function listenIncrement() { await click(incrementBtn);
counter += 1; counterEl.innerHTML = counter; listenIncrement(); }
let counter = 0; async function listenIncrement() { await click(incrementBtn);
counter += 1; counterEl.innerHTML = counter; listenIncrement(); }
let counter = 0; async function listenIncrement() { await click(incrementBtn);
counter += 1; counterEl.innerHTML = counter; listenIncrement(); }
let counter = 0; async function listenIncrement() { await click(incrementBtn);
counter += 1; counterEl.innerHTML = counter; listenIncrement(); } ×
const source = Observable.merge( Observable.fromEvent(incrementBtn, 'click').mapTo(1), Observable.fromEvent(decrementBtn, 'click').mapTo(-1), ); source
.scan((acc, curr) => acc + curr, 0) .subscribe((counter) => { counterEl.innerHTML = counter; });
RxJS
[ 1, 2, 3, 4, 5 ] Arrays Sequences in
space
Observables Sequences in time 1 2 3 4 5
Reactive
Reactive 3
const { Observable } = require('rxjs'); const source = Observable.of(1,
2, 3); source.subscribe(x => console.log(x)); // 1 // 2 // 3
const { Observable } = require('rxjs'); const source = Observable.of(1,
2, 3); source.subscribe(x => console.log(x)); // 1 // 2 // 3
const { Observable } = require('rxjs'); const source = Observable.of(1,
2, 3); source.subscribe(x => console.log(x)); // 1 // 2 // 3
const { Observable } = require('rxjs'); const source = Observable.of(1,
2, 3); source.subscribe(x => console.log(x)); // 1 // 2 // 3
Observable.of(1, 2, 3) .subscribe(x => console.log(x)); console.log subscribe
Observable.of(1, 2, 3) .subscribe(x => console.log(x)); console.log subscribe 1
Observable.of(1, 2, 3) .subscribe(x => console.log(x)); console.log subscribe 1
Observable.of(1, 2, 3) .subscribe(x => console.log(x)); console.log subscribe
Observable.of(1, 2, 3) .subscribe(x => console.log(x)); console.log subscribe 2
Observable.of(1, 2, 3) .subscribe(x => console.log(x)); console.log subscribe 2
Observable.of(1, 2, 3) .subscribe(x => console.log(x)); console.log subscribe
Observable.of(1, 2, 3) .subscribe(x => console.log(x)); console.log subscribe 3
Observable.of(1, 2, 3) .subscribe(x => console.log(x)); console.log subscribe 3
Declarative Transformation Operate on events
Observable.of(1, 2, 3) .map(n => n * 2) .subscribe(x =>
console.log(x)); // 2 // 4 // 6
Observable.of(1, 2, 3) .map(n => n * 2) .subscribe(x =>
console.log(x)); // 2 // 4 // 6
Observable.of(1, 2, 3) console.log n * 2 map subscribe
Observable.of(1, 2, 3) console.log n * 2 map subscribe 1
Observable.of(1, 2, 3) console.log n * 2 map subscribe 1
Observable.of(1, 2, 3) console.log n * 2 map subscribe 2
Observable.of(1, 2, 3) console.log n * 2 map subscribe 2
Observable.of(1, 2, 3) console.log n * 2 map subscribe
Observable.of(1, 2, 3) console.log n * 2 map subscribe 2
Observable.of(1, 2, 3) console.log n * 2 map subscribe 2
Observable.of(1, 2, 3) console.log n * 2 map subscribe 4
Observable.of(1, 2, 3) console.log n * 2 map subscribe 4
Observable.of(1, 2, 3) console.log n * 2 map subscribe
Observable.of(1, 2, 3) console.log n * 2 map subscribe 3
Observable.of(1, 2, 3) console.log n * 2 map subscribe 3
Observable.of(1, 2, 3) console.log n * 2 map subscribe 6
Observable.of(1, 2, 3) console.log n * 2 map subscribe 6
Lazy Transformation Do only as much work as needed
Observable.range(1, 100) .map(n => n * 2) .filter(n => n
> 4) .take(2) .subscribe(x => console.log(x)); // 6 // 8
Observable.range(1, 100) .map(n => n * 2) .filter(n => n
> 4) .take(2) .subscribe(x => console.log(x)); // 6 // 8
Observable.range(1, 100) .map(n => n * 2) .filter(n => n
> 4) .take(2) .subscribe(x => console.log(x)); // 6 // 8
Observable.range(1, 100) .map(n => n * 2) .filter(n => n
> 4) .take(2) .subscribe(x => console.log(x)); // 6 // 8
Observable.range(1, 100) .map(n => n * 2) .filter(n => n
> 4) .take(2) .subscribe(x => console.log(x)); // 6 // 8
console.log n * 2 map subscribe n > 4 Observable.range(1,
100) filter 2 take
console.log n * 2 map subscribe n > 4 1
Observable.range(1, 100) filter 2 take
console.log n * 2 map subscribe n > 4 1
Observable.range(1, 100) filter 2 take
console.log n * 2 map subscribe n > 4 2
Observable.range(1, 100) filter 2 take
console.log n * 2 map subscribe n > 4 2
Observable.range(1, 100) filter 2 take
console.log n * 2 map subscribe n > 4 2
Observable.range(1, 100) filter 2 take ×
console.log n * 2 map subscribe n > 4 Observable.range(1,
100) filter 2 take
console.log n * 2 map subscribe n > 4 2
Observable.range(1, 100) filter 2 take
console.log n * 2 map subscribe n > 4 2
Observable.range(1, 100) filter 2 take
console.log n * 2 map subscribe n > 4 4
Observable.range(1, 100) filter 2 take
console.log n * 2 map subscribe n > 4 Observable.range(1,
100) filter 2 take 4
console.log n * 2 map subscribe n > 4 Observable.range(1,
100) filter 2 take 4 ×
console.log n * 2 map subscribe n > 4 Observable.range(1,
100) filter 2 take
console.log n * 2 map subscribe n > 4 3
Observable.range(1, 100) filter 2 take
console.log n * 2 map subscribe n > 4 3
Observable.range(1, 100) filter 2 take
console.log n * 2 map subscribe n > 4 6
Observable.range(1, 100) filter 2 take
console.log n * 2 map subscribe n > 4 Observable.range(1,
100) filter 2 take 6
✓ console.log n * 2 map subscribe n > 4
Observable.range(1, 100) filter 2 take 6
console.log n * 2 map subscribe n > 4 Observable.range(1,
100) filter 2 take 6
console.log n * 2 map subscribe n > 4 Observable.range(1,
100) filter 1 take 6
console.log n * 2 map subscribe n > 4 Observable.range(1,
100) filter 1 take
console.log n * 2 map subscribe n > 4 4
Observable.range(1, 100) filter 1 take
console.log n * 2 map subscribe n > 4 4
Observable.range(1, 100) filter 1 take
console.log n * 2 map subscribe n > 4 8
Observable.range(1, 100) filter 1 take
console.log n * 2 map subscribe n > 4 Observable.range(1,
100) filter 1 take 8
✓ console.log n * 2 map subscribe n > 4
Observable.range(1, 100) filter 1 take 8
console.log n * 2 map subscribe n > 4 Observable.range(1,
100) filter 1 take 8
console.log n * 2 map subscribe n > 4 Observable.range(1,
100) filter 0 take 8
DOM Events
Observable.fromEvent(incrementBtn, 'click') .mapTo(1) .scan((acc, curr) => acc + curr, 0)
.subscribe((counter) => { counterEl.innerHTML = counter; });
Observable.fromEvent(incrementBtn, 'click') .mapTo(1) .scan((acc, curr) => acc + curr, 0)
.subscribe((counter) => { counterEl.innerHTML = counter; });
Observable.fromEvent(incrementBtn, 'click') .mapTo(1) .scan((acc, curr) => acc + curr, 0)
.subscribe((counter) => { counterEl.innerHTML = counter; });
Observable.fromEvent(incrementBtn, 'click') .mapTo(1) .scan((acc, curr) => acc + curr, 0)
.subscribe((counter) => { counterEl.innerHTML = counter; });
Observable.fromEvent(incrementBtn, 'click') .mapTo(1) .scan((acc, curr) => acc + curr, 0)
.subscribe((counter) => { counterEl.innerHTML = counter; });
Observable.fromEvent(incrementBtn, 'click') .mapTo(1) .scan((acc, curr) => acc + curr, 0)
.subscribe((counter) => { counterEl.innerHTML = counter; });
Observable.fromEvent(incrementBtn, 'click') .mapTo(1) .scan((acc, curr) => acc + curr, 0)
.subscribe((counter) => { counterEl.innerHTML = counter; });
.scan((acc, curr) => acc + curr, 0) Accumulated counter value
.scan((acc, curr) => acc + curr, 0) Current event value
(1)
.scan((acc, curr) => acc + curr, 0) Return new accumulated
counter value
.scan((acc, curr) => acc + curr, 0) Initial counter value
counterEl.innerHTML = counter acc + curr 1 mapTo subscribe 0
scan
acc + curr 1 mapTo subscribe e scan 0 counterEl.innerHTML
= counter
1 mapTo subscribe e scan acc + curr 0 counterEl.innerHTML
= counter
1 mapTo subscribe 1 scan acc + curr 0 counterEl.innerHTML
= counter
1 mapTo subscribe scan 1 acc + curr 0 counterEl.innerHTML
= counter
1 mapTo subscribe scan 1 acc + curr 1 Counter
value counterEl.innerHTML = counter
1 mapTo subscribe scan 1 acc + curr 1 counterEl.innerHTML
= counter
acc + curr 1 mapTo subscribe scan 1 counterEl.innerHTML =
counter
acc + curr 1 mapTo subscribe e scan 1 counterEl.innerHTML
= counter
1 mapTo subscribe e scan acc + curr 1 counterEl.innerHTML
= counter
1 mapTo subscribe 1 scan acc + curr 1 counterEl.innerHTML
= counter
1 mapTo subscribe scan 1 acc + curr 1 counterEl.innerHTML
= counter
1 mapTo subscribe scan 2 acc + curr 2 Counter
value counterEl.innerHTML = counter
1 mapTo subscribe scan 2 acc + curr 2 counterEl.innerHTML
= counter
const source = Observable.merge( Observable.fromEvent(incrementBtn, 'click').mapTo(1), Observable.fromEvent(decrementBtn, 'click').mapTo(-1), ); source
.scan((acc, curr) => acc + curr, 0) .subscribe((counter) => { counterEl.innerHTML = counter; });
const source = Observable.merge( Observable.fromEvent(incrementBtn, 'click').mapTo(1), Observable.fromEvent(decrementBtn, 'click').mapTo(-1), ); source
.scan((acc, curr) => acc + curr, 0) .subscribe((counter) => { counterEl.innerHTML = counter; });
const source = Observable.merge( Observable.fromEvent(incrementBtn, 'click').mapTo(1), Observable.fromEvent(decrementBtn, 'click').mapTo(-1), ); source
.scan((acc, curr) => acc + curr, 0) .subscribe((counter) => { counterEl.innerHTML = counter; });
const source = Observable.merge( Observable.fromEvent(incrementBtn, 'click').mapTo(1), Observable.fromEvent(decrementBtn, 'click').mapTo(-1), ); source
.scan((acc, curr) => acc + curr, 0) .subscribe((counter) => { counterEl.innerHTML = counter; });
acc + curr 1 mapTo subscribe 0 scan -1 mapTo
Increment Decrement counterEl.innerHTML = counter
acc + curr 1 mapTo subscribe 0 scan -1 mapTo
Increment Decrement e counterEl.innerHTML = counter
acc + curr 1 mapTo subscribe 0 scan -1 mapTo
Increment Decrement e counterEl.innerHTML = counter
acc + curr 1 mapTo subscribe 0 scan -1 mapTo
Increment Decrement 1 counterEl.innerHTML = counter
acc + curr 1 mapTo subscribe 0 scan -1 mapTo
Increment Decrement 1 counterEl.innerHTML = counter
acc + curr 1 mapTo subscribe 1 scan -1 mapTo
Increment Decrement 1 counterEl.innerHTML = counter
acc + curr 1 mapTo subscribe 1 scan -1 mapTo
Increment Decrement 1 counterEl.innerHTML = counter
acc + curr 1 mapTo subscribe 1 scan -1 mapTo
Increment Decrement counterEl.innerHTML = counter
acc + curr 1 mapTo subscribe 1 scan -1 mapTo
Increment Decrement e counterEl.innerHTML = counter
acc + curr 1 mapTo subscribe 1 scan -1 mapTo
Increment Decrement e counterEl.innerHTML = counter
acc + curr 1 mapTo subscribe 1 scan -1 mapTo
Increment Decrement -1 counterEl.innerHTML = counter
acc + curr 1 mapTo subscribe 1 scan -1 mapTo
Increment Decrement -1 counterEl.innerHTML = counter
acc + curr 1 mapTo subscribe 0 scan -1 mapTo
Increment Decrement 0 counterEl.innerHTML = counter
acc + curr 1 mapTo subscribe 0 scan -1 mapTo
Increment Decrement 0 counterEl.innerHTML = counter
Async HTTP
Promise fetchOrders() .then((orders) => { orders.forEach((order) => { console.log(order); });
});
Promise fetchOrders() .then((orders) => { orders.forEach((order) => { console.log(order); });
});
Observable fetchOrders() .subscribe((orders) => { orders.forEach((order) => { console.log(order); });
});
Why Observables?
Promise fetchOrders() .then(orders => orders.filter( order => order.customerName === 'Tucker'
)) .then(orders => orders.map(order => order.id)) .then((orderIds) => { orderIds.forEach(id => console.log(id)); });
Promise fetchOrders() .then(orders => orders.filter( order => order.customerName === 'Tucker'
)) .then(orders => orders.map(order => order.id)) .then((orderIds) => { orderIds.forEach(id => console.log(id)); });
Promise fetchOrders() .then(orders => orders.filter( order => order.customerName === 'Tucker'
)) .then(orders => orders.map(order => order.id)) .then((orderIds) => { orderIds.forEach(id => console.log(id)); });
Promise fetchOrders() .then(orders => orders.filter( order => order.customerName === 'Tucker'
)) .then(orders => orders.map(order => order.id)) .then((orderIds) => { orderIds.forEach(id => console.log(id)); });
Async async function printCustomerNames() { const orders = await fetchOrders();
const filtered = orders.filter( order => order.customerName === 'Tucker' ); const orderIds = filtered.map(order => order.id); orderIds.forEach(id => console.log(id)); }
Async async function printCustomerNames() { const orders = await fetchOrders();
const filtered = orders.filter( order => order.customerName === 'Tucker' ); const orderIds = filtered.map(order => order.id); orderIds.forEach(id => console.log(id)); }
Async async function printCustomerNames() { const orders = await fetchOrders();
const filtered = orders.filter( order => order.customerName === 'Tucker' ); const orderIds = filtered.map(order => order.id); orderIds.forEach(id => console.log(id)); }
fetchOrders() .mergeAll() .filter( order => order.customerName === 'Tucker' ) .map(order
=> order.id) .subscribe(id => console.log(id)); Observable
fetchOrders() .mergeAll() .filter( order => order.customerName === 'Tucker' ) .map(order
=> order.id) .subscribe(id => console.log(id)); Observable
fetchOrders() .mergeAll() .filter( order => order.customerName === 'Tucker' ) .map(order
=> order.id) .subscribe(id => console.log(id)); Observable
fetchOrders() .mergeAll() .filter( order => order.customerName === 'Tucker' ) .map(order
=> order.id) .subscribe(id => console.log(id)); Observable
fetchOrders() .mergeAll() .filter( order => order.customerName === 'Tucker' ) .map(order
=> order.id) .subscribe(id => console.log(id)); Observable
Cancellation
const promise = fetchOrders() .then((orders) => { orders.foreach((order) => {
console.log(order); }); }); promise.cancel(); Promise
const promise = fetchOrders() .then((orders) => { orders.foreach((order) => {
console.log(order); }); }); promise.cancel(); Promise
const promise = fetchOrders() .then((orders) => { orders.foreach((order) => {
console.log(order); }); }); promise.cancel(); Promise ×
Observable const subscription = fetchOrders() .subscribe((orders) => { orders.forEach((order) =>
{ console.log(order); }); }); subscription.unsubscribe();
Observable const subscription = fetchOrders() .subscribe((orders) => { orders.forEach((order) =>
{ console.log(order); }); }); subscription.unsubscribe();
console.log subscribe fetchOrders() .subscribe(...)
console.log subscribe fetchOrders() .subscribe(...)
subscription.unsubscribe(); console.log subscribe × fetchOrders() .subscribe(...) ×
Create HTTP Requests
function fetchOrders() { return Observable.ajax.get('/orders'); } Built-in AJAX
function fetchOrders() { return Observable.ajax.get('/orders'); } Built-in AJAX
function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {
subscriber.next(orders); subscriber.complete(); }); }); } Custom Observable Creation
function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {
subscriber.next(orders); subscriber.complete(); }); }); } Custom Observable Creation
function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {
subscriber.next(orders); subscriber.complete(); }); }); } Custom Observable Creation
function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {
subscriber.next(orders); subscriber.complete(); }); }); } Custom Observable Creation
function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {
subscriber.next(orders); subscriber.complete(); }); }); } Custom Observable Creation
function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {
subscriber.next(orders); subscriber.complete(); }); }); } Custom Observable Creation
function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {
subscriber.next(orders); subscriber.complete(); }); }); } Custom Observable Creation ?
Observable.of(1, 2, 3) .subscribe(x => console.log(x));
Observable.of(1, 2, 3) .subscribe(x => console.log(x)); Subscriber
Observable.of(1, 2, 3) .subscribe({ next: x => console.log(x), });
Observable.of(1, 2, 3) .subscribe({ next: x => console.log(x), complete: ()
=> console.log('Done!'), }); // 1 // 2 // 3 // Done!
function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {
subscriber.next(orders); subscriber.complete(); }); }); } fetchOrders().mergeAll().subscribe({ next: x => console.log(x), complete: () => console.log('Done!'), });
function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {
subscriber.next(orders); subscriber.complete(); }); }); } fetchOrders().mergeAll().subscribe({ next: x => console.log(x), complete: () => console.log('Done!'), });
function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {
subscriber.next(orders); subscriber.complete(); }); }); } fetchOrders().mergeAll().subscribe({ next: x => console.log(x), complete: () => console.log('Done!'), });
function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {
subscriber.next(orders); subscriber.complete(); }); }); } fetchOrders().mergeAll() .subscribe(x => console.log(x));
function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {
subscriber.next(orders); subscriber.complete(); }); }); } fetchOrders().mergeAll() .subscribe(x => console.log(x)); No complete callback
function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {
subscriber.next(orders); subscriber.complete(); }); }); } fetchOrders().mergeAll() .subscribe(x => console.log(x)); Safe No complete callback
const source = Observable.create((subscriber) => { subscriber.next(1); subscriber.error(new Error('Uh oh'));
subscriber.next(2); }); source.subscribe({ next: x => console.log(x), complete: () => console.log('Done!'), error: e => console.error(e), }); // 1 // Error: Uh oh
const source = Observable.create((subscriber) => { subscriber.next(1); subscriber.error(new Error('Uh oh'));
subscriber.next(2); }); source.subscribe({ next: x => console.log(x), complete: () => console.log('Done!'), error: e => console.error(e), }); // 1 // Error: Uh oh
const source = Observable.create((subscriber) => { subscriber.next(1); subscriber.error(new Error('Uh oh'));
subscriber.next(2); }); source.subscribe({ next: x => console.log(x), complete: () => console.log('Done!'), error: e => console.error(e), }); // 1 // Error: Uh oh
const source = Observable.create((subscriber) => { subscriber.next(1); subscriber.error(new Error('Uh oh'));
subscriber.next(2); }); source.subscribe({ next: x => console.log(x), complete: () => console.log('Done!'), error: e => console.error(e), }); // 1 // Error: Uh oh Never called
Error Handling
Error Error Error Error Promises
const source = Observable.create((subscriber) => { subscriber.next(1); subscriber.error(new Error('Uh oh'));
subscriber.next(2); }); source.subscribe({ next: x => console.log(x), complete: () => console.log('Done!'), error: e => console.error(e), });
const source = Observable.create((subscriber) => { subscriber.next(1); subscriber.error(new Error('Uh oh'));
subscriber.next(2); }); source.subscribe(x => console.log(x)); ?
const source = Observable.create((subscriber) => { subscriber.next(1); subscriber.error(new Error('Uh oh'));
subscriber.next(2); }); source.subscribe(x => console.log(x));
fetchOrders() .catch((e) => { logError(e); return Observable.of([]); }) .subscribe(x =>
console.log(x));
fetchOrders() .catch((e) => { logError(e); return Observable.of([]); }) .subscribe(x =>
console.log(x));
fetchOrders() .catch((e) => { logError(e); return Observable.of([]); }) .subscribe(x =>
console.log(x));
fetchOrders() .catch((e) => { logError(e); return Observable.of([]); }) .subscribe(x =>
console.log(x));
fetchOrders() .catch((e) => { logError(e); return legacyFetchOrders() .catch((e2) => {
logError(e2); return Observable.of([]); }) }) .subscribe(x => console.log(x));
fetchOrders() .catch((e) => { logError(e); return legacyFetchOrders() .catch((e2) => {
logError(e2); return Observable.of([]); }) }) .subscribe(x => console.log(x));
fetchOrders() .catch((e) => { logError(e); return legacyFetchOrders() .catch((e2) => {
logError(e2); return Observable.of([]); }) }) .subscribe(x => console.log(x));
console.log catch subscribe legacyFetchOrders() fetchOrders() catch Observable.of([])
console.log catch subscribe legacyFetchOrders() fetchOrders() catch Observable.of([]) ✓
console.log catch subscribe legacyFetchOrders() fetchOrders() catch Observable.of([]) ✓
console.log catch subscribe legacyFetchOrders() fetchOrders() catch Observable.of([])
console.log catch subscribe legacyFetchOrders() fetchOrders() catch Observable.of([]) ×
console.log catch subscribe legacyFetchOrders() fetchOrders() catch Observable.of([]) ×
console.log catch subscribe legacyFetchOrders() fetchOrders() catch Observable.of([]) ×
console.log catch subscribe legacyFetchOrders() fetchOrders() catch Observable.of([]) × ✓
console.log catch subscribe legacyFetchOrders() fetchOrders() catch Observable.of([]) × ✓
console.log catch subscribe legacyFetchOrders() fetchOrders() catch Observable.of([])
console.log catch subscribe legacyFetchOrders() fetchOrders() catch Observable.of([]) ×
console.log catch subscribe legacyFetchOrders() fetchOrders() catch Observable.of([]) ×
console.log catch subscribe legacyFetchOrders() fetchOrders() catch Observable.of([]) ×
console.log catch subscribe legacyFetchOrders() fetchOrders() catch Observable.of([]) × ×
console.log catch subscribe legacyFetchOrders() fetchOrders() catch Observable.of([]) × ×
console.log catch subscribe legacyFetchOrders() fetchOrders() catch Observable.of([]) × × []
Clean Up
function fetchOrdersStream() { return Observable.create((subscriber) => { const socket =
new WebSocket('ws://example.com'); socket.addEventListener('message', (event) => { subscriber.next(event.data); }); return () => { socket.close(); }; }); }
function fetchOrdersStream() { return Observable.create((subscriber) => { const socket =
new WebSocket('ws://example.com'); socket.addEventListener('message', (event) => { subscriber.next(event.data); }); return () => { socket.close(); }; }); }
function fetchOrdersStream() { return Observable.create((subscriber) => { const socket =
new WebSocket('ws://example.com'); socket.addEventListener('message', (event) => { subscriber.next(event.data); }); return () => { socket.close(); }; }); }
function fetchOrdersStream() { return Observable.create((subscriber) => { const socket =
new WebSocket('ws://example.com'); socket.addEventListener('message', (event) => { subscriber.next(event.data); }); return () => { socket.close(); }; }); } Called when subscription unsubscribes
function fetchOrdersStream() { return Observable.create((subscriber) => { const socket =
new WebSocket('ws://example.com'); socket.addEventListener('message', (event) => { subscriber.next(event.data); }); return () => { socket.close(); }; }); } Close socket, deallocate resources, etc.
Lazy Subscriptions
const p1 = fetchOrders(); const p2 = fetchOrders(); const p3
= fetchOrders(); Promises
const p1 = fetchOrders(); const p2 = fetchOrders(); const p3
= fetchOrders(); Promises Immediate
Observables const o1 = fetchOrders(); const o2 = fetchOrders(); const
o3 = fetchOrders(); o1.subscribe(); o2.subscribe(); o3.subscribe();
Observables Lazy const o1 = fetchOrders(); const o2 = fetchOrders();
const o3 = fetchOrders(); o1.subscribe(); o2.subscribe(); o3.subscribe();
Observables Lazy const o1 = fetchOrders(); const o2 = fetchOrders();
const o3 = fetchOrders(); o1.subscribe(); o2.subscribe(); o3.subscribe(); Issue Request
const o1 = fetchOrders(); o1.subscribe(); o1.subscribe(); o1.subscribe();
const o1 = fetchOrders(); o1.subscribe(); o1.subscribe(); o1.subscribe(); Pure
const o1 = fetchOrders(); o1.subscribe(); o1.subscribe(); o1.subscribe(); Side Effects Pure
Hot Cold vs.
Observable creates the source Cold
Best for one-off unique requests. Cold
function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {
subscriber.next(orders); subscriber.complete(); }); }); }
function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {
subscriber.next(orders); subscriber.complete(); }); }); } Resource requested/created at subscription time
Hot Observable closes over the source
Hot Best for multicasting and sharing resources.
const socket = new WebSocket('ws://example.com'); const stream = Observable.create((subscriber) =>
{ socket.addEventListener('message', (event) => { subscriber.next(event.data); }); });
const socket = new WebSocket('ws://example.com'); const stream = Observable.create((subscriber) =>
{ socket.addEventListener('message', (event) => { subscriber.next(event.data); }); }); Resource created outside subscription
const socket = new WebSocket('ws://example.com'); const stream = Observable.create((subscriber) =>
{ socket.addEventListener('message', (event) => { subscriber.next(event.data); }); }); Close over existing resource when subscribing
const sub1 = stream.subscribe(x => console.log(x)); const sub2 = stream.subscribe(x
=> console.log(x));
const sub1 = stream.subscribe(x => console.log(x)); const sub2 = stream.subscribe(x
=> console.log(x)); Shared stream of data
fetchOrders() .mergeAll() .filter( order => order.customerName === 'Tucker' ) .map(order
=> order.id) .subscribe(id => console.log(id)); Recall
fetchOrders() .mergeAll() .filter( order => order.customerName === 'Tucker' ) .map(order
=> order.id) .subscribe(id => console.log(id)); ? Recall
Observable.of([1, 2, 3]) .subscribe(x => console.log(x)); // [ 1, 2,
3 ]
Observable.of([1, 2, 3]) .subscribe(x => console.log(x)); // [ 1, 2,
3 ]
Observable.of([1, 2, 3]) .subscribe(x => console.log(x)); // [ 1, 2,
3 ]
Observable.of([1, 2, 3]) .mergeAll() .subscribe(x => console.log(x)); // 1 //
2 // 3
Observable.of([1, 2, 3]) .mergeAll() .subscribe(x => console.log(x)); // 1 //
2 // 3 Flatten
Observable.of([1, 2, 3]) .mergeAll() .subscribe(x => console.log(x)); // 1 //
2 // 3
subscribe mergeAll console.log [1, 2, 3]
subscribe mergeAll console.log [1, 2, 3]
subscribe mergeAll console.log [2, 3] 1
subscribe mergeAll console.log [2, 3] 1
subscribe mergeAll console.log [3] 2
subscribe mergeAll console.log [3] 2
subscribe mergeAll console.log 3
subscribe mergeAll console.log 3
Higher Order Observables
Observable.of(1, 2, 3) .map(n => n * 2) .subscribe(x =>
console.log(x)); // 2 // 4 // 6
Observable.of(1, 2, 3) .map(n => n * 2) .subscribe(x =>
console.log(x)); // 2 // 4 // 6 Delay?
Observable.of(1, 2, 3) .map(n => Observable.of(n * 2)) .subscribe(x =>
console.log(x)); // ScalarObservable { value: 2 } // ScalarObservable { value: 4 } // ScalarObservable { value: 6 }
Observable.of(1, 2, 3) .map(n => Observable.of(n * 2)) .subscribe(x =>
console.log(x)); // ScalarObservable { value: 2 } // ScalarObservable { value: 4 } // ScalarObservable { value: 6 }
Observable.of(1, 2, 3) .map(n => Observable.of(n * 2)) .subscribe(x =>
console.log(x)); // ScalarObservable { value: 2 } // ScalarObservable { value: 4 } // ScalarObservable { value: 6 }
Observable.of(1, 2, 3) .map(n => Observable.of(n * 2)) .mergeAll() .subscribe(x
=> console.log(x)); // 2 // 4 // 6
Observable.of(1, 2, 3) .map(n => Observable.of(n * 2)) .mergeAll() .subscribe(x
=> console.log(x)); // 2 // 4 // 6
Observable.of(1, 2, 3) .mergeMap(n => Observable.of(n * 2)) .subscribe(x =>
console.log(x)); // 2 // 4 // 6
subscribe mergeMap console.log Observable.of(n * 2)
subscribe mergeMap console.log Observable.of(n * 2) 1
subscribe mergeMap console.log Observable.of(n * 2) 1
subscribe mergeMap console.log Observable.of(n * 2) Observable.of(2) subscribe 2
subscribe mergeMap console.log Observable.of(n * 2) Observable.of(2) subscribe 2
subscribe mergeMap console.log Observable.of(n * 2) Observable.of(2) subscribe 2
Observable.of(1) .delay(1000) .subscribe(x => console.log(x)) // <tick> // 1
Observable.of(1) .delay(1000) .subscribe(x => console.log(x)) // <tick> // 1
Observable.of(1) .delay(1000) .subscribe(x => console.log(x)) // <tick> // 1
Observable.of(1) .delay(1000) .subscribe(x => console.log(x)) // <tick> // 1
Observable.of(1, 2, 3) .mergeMap(n => ( Observable.of(n * 2).delay(1000) ))
.subscribe(x => console.log(x)); // <tick> // 2 // 4 // 6
Observable.of(1, 2, 3) .mergeMap(n => ( Observable.of(n * 2).delay(1000) ))
.subscribe(x => console.log(x)); // <tick> // 2 // 4 // 6
Observable.of(1, 2, 3) .mergeMap(n => ( Observable.of(n * 2).delay(1000) ))
.subscribe(x => console.log(x)); // <tick> // 2 // 4 // 6
Observable.of(1, 2, 3) .mergeMap(n => ( Observable.of(n * 2).delay(1000) ))
.subscribe(x => console.log(x)); // <tick> // 2 // 4 // 6 ?
Concurrency
subscribe mergeMap console.log Observable.of(n * 2)
subscribe mergeMap console.log Observable.of(n * 2) 1
subscribe mergeMap console.log Observable.of(n * 2) 1
subscribe mergeMap console.log Observable.of(n * 2) 2
subscribe mergeMap console.log Observable.of(n * 2) 2 2
subscribe mergeMap console.log Observable.of(n * 2) 2 2
subscribe mergeMap console.log Observable.of(n * 2) 2 4
subscribe mergeMap console.log Observable.of(n * 2) 3 2 4
subscribe mergeMap console.log Observable.of(n * 2) 3 2 4
subscribe mergeMap console.log Observable.of(n * 2) 2 4 6
subscribe mergeMap console.log Observable.of(n * 2) 2 4 6
subscribe mergeMap console.log Observable.of(n * 2) 4 6 2
subscribe mergeMap console.log Observable.of(n * 2) 6 4
subscribe mergeMap console.log Observable.of(n * 2) 6
Observable.of(1, 2, 3) .mergeMap(n => ( Observable.of(n * 2).delay(1000) ),
1) .subscribe(x => console.log(x)); // <tick> // 2 // <tick> // 4 // <tick> // 6
Observable.of(1, 2, 3) .mergeMap(n => ( Observable.of(n * 2).delay(1000) ),
1) .subscribe(x => console.log(x)); // <tick> // 2 // <tick> // 4 // <tick> // 6
Observable.of(1, 2, 3) .mergeMap(n => ( Observable.of(n * 2).delay(1000) ),
1) .subscribe(x => console.log(x)); // <tick> // 2 // <tick> // 4 // <tick> // 6
Observable.of(1, 2, 3) .mergeMap(n => ( Observable.of(n * 2).delay(1000) ),
1) .subscribe(x => console.log(x)); // <tick> // 2 // <tick> // 4 // <tick> // 6
Observable.of(1, 2, 3) .mergeMap(n => ( Observable.of(n * 2).delay(1000) ),
1) .subscribe(x => console.log(x)); // <tick> // 2 // <tick> // 4 // <tick> // 6
Observable.of(1, 2, 3) .concatMap(n => ( Observable.of(n * 2).delay(1000) ))
.subscribe(x => console.log(x)); // <tick> // 2 // <tick> // 4 // <tick> // 6
subscribe concatMap console.log Observable.of(n * 2)
subscribe console.log Observable.of(n * 2) 1 concatMap
subscribe console.log Observable.of(n * 2) 1 concatMap
subscribe console.log Observable.of(n * 2) 2 concatMap
subscribe console.log Observable.of(n * 2) 2 concatMap
subscribe console.log Observable.of(n * 2) 2 concatMap
subscribe console.log Observable.of(n * 2) 2 concatMap
subscribe console.log Observable.of(n * 2) 2 concatMap
subscribe console.log Observable.of(n * 2) 4 concatMap
subscribe console.log Observable.of(n * 2) 4 concatMap
subscribe console.log Observable.of(n * 2) 4 concatMap
subscribe console.log Observable.of(n * 2) 3 concatMap
subscribe console.log Observable.of(n * 2) 3 concatMap
subscribe console.log Observable.of(n * 2) 6 concatMap
subscribe console.log Observable.of(n * 2) 6 concatMap
subscribe console.log Observable.of(n * 2) 6 concatMap
Rate Limiting
Observable.of(1, 2, 3) .concatMap((id) => { const url = `/orders/${id}`;
return Observable.ajax.get(url) .delay(1000); }) .pluck('response') .bufferCount(3) .subscribe(x => console.log(x)); // [ { id: '1', name: 'Order 1' }, // { id: '2', name: 'Order 2' }, // { id: '3', name: 'Order 3' } ]
Observable.of(1, 2, 3) .concatMap((id) => { const url = `/orders/${id}`;
return Observable.ajax.get(url) .delay(1000); }) .pluck('response') .bufferCount(3) .subscribe(x => console.log(x)); // [ { id: '1', name: 'Order 1' }, // { id: '2', name: 'Order 2' }, // { id: '3', name: 'Order 3' } ]
Observable.of(1, 2, 3) .concatMap((id) => { const url = `/orders/${id}`;
return Observable.ajax.get(url) .delay(1000); }) .pluck('response') .bufferCount(3) .subscribe(x => console.log(x)); // [ { id: '1', name: 'Order 1' }, // { id: '2', name: 'Order 2' }, // { id: '3', name: 'Order 3' } ]
Observable.of(1, 2, 3) .concatMap((id) => { const url = `/orders/${id}`;
return Observable.ajax.get(url) .delay(1000); }) .pluck('response') .bufferCount(3) .subscribe(x => console.log(x)); // [ { id: '1', name: 'Order 1' }, // { id: '2', name: 'Order 2' }, // { id: '3', name: 'Order 3' } ]
Observable .ajax .get(url) subscribe console.log pluck bufferCount 'response' 3 concatMap
Observable .ajax .get(url) subscribe concatMap console.log pluck bufferCount 'response' 3
1
Observable .ajax .get(url) subscribe concatMap console.log pluck bufferCount 'response' 3
Observable .ajax .get(url) subscribe concatMap console.log pluck bufferCount 'response' 3
R Rate limit next request
Observable .ajax .get(url) subscribe concatMap console.log pluck bufferCount 'response' 3
R
Observable .ajax .get(url) subscribe concatMap console.log pluck bufferCount 'response' 3
R
Observable .ajax .get(url) subscribe concatMap console.log pluck bufferCount 'response' 3
O1
Observable .ajax .get(url) subscribe concatMap console.log pluck bufferCount 'response' 2
O1
Observable .ajax .get(url) subscribe concatMap console.log pluck bufferCount 'response' 2
O1 +
Observable .ajax .get(url) subscribe concatMap console.log pluck bufferCount 'response' 1
O1 O2
Observable .ajax .get(url) subscribe concatMap console.log pluck bufferCount 'response' 1
O1 O2 +
Observable .ajax .get(url) subscribe concatMap console.log pluck bufferCount 'response' 0
O1 O2 O3
Observable .ajax .get(url) subscribe concatMap console.log […] pluck bufferCount 'response'
0
Sequential vs. Parallel
const promise = Promise.all([ fetchOrder(1), fetchOrder(2), fetchOrder(3), ]); promise.then(x =>
console.log(x)); // [ { id: '1', name: 'Order 1' }, // { id: '2', name: 'Order 2' }, // { id: '3', name: 'Order 3' } ]
const promise = Promise.all([ fetchOrder(1), fetchOrder(2), fetchOrder(3), ]); promise.then(x =>
console.log(x)); // [ { id: '1', name: 'Order 1' }, // { id: '2', name: 'Order 2' }, // { id: '3', name: 'Order 3' } ]
const source = Observable.forkJoin( fetchOrder(1), fetchOrder(2), fetchOrder(3) ); source.subscribe(x =>
console.log(x)); // [ { response: { id: 1, name: 'Order 1' } }, // { response: { id: 2, name: 'Order 2' } }, // { response: { id: 3, name: 'Order 3' } } ]
const source = Observable.forkJoin( fetchOrder(1), fetchOrder(2), fetchOrder(3) ); source.subscribe(x =>
console.log(x)); // [ { response: { id: 1, name: 'Order 1' } }, // { response: { id: 2, name: 'Order 2' } }, // { response: { id: 3, name: 'Order 3' } } ]
const source = Observable.forkJoin( fetchOrder(1), fetchOrder(2), fetchOrder(3) ); source.subscribe(x =>
console.log(x)); // [ { response: { id: 1, name: 'Order 1' } }, // { response: { id: 2, name: 'Order 2' } }, // { response: { id: 3, name: 'Order 3' } } ] Observable or Promise
fetchOrder(1) fetchOrder(2) fetchOrder(3) forkJoin subscribe console.log
fetchOrder(1) fetchOrder(2) fetchOrder(3) forkJoin subscribe console.log R1
fetchOrder(1) fetchOrder(2) fetchOrder(3) forkJoin R1 subscribe console.log
fetchOrder(1) fetchOrder(2) fetchOrder(3) forkJoin R1 subscribe console.log R3
fetchOrder(1) fetchOrder(2) fetchOrder(3) forkJoin R1 R3 subscribe console.log
fetchOrder(1) fetchOrder(2) fetchOrder(3) forkJoin R1 R3 subscribe console.log R2
fetchOrder(1) fetchOrder(2) fetchOrder(3) forkJoin R1 R2 R3 subscribe console.log
fetchOrder(1) fetchOrder(2) fetchOrder(3) forkJoin subscribe console.log R1 R2 R3
• Declarative, lazy operations • Expressive event management • No
more error swallowing • Rate limiting and concurrent processing
• Declarative, lazy operations • Expressive event management • No
more error swallowing • Rate limiting and concurrent processing ✓ ✓ ✓ ✓
Resources
Browser compatibility: kangax.github.io/compat-table/es2016plus Node compatibility: node.green Async
Async babeljs.io
Observables github.com/ReactiveX/rxjs RxJS
Observables ECMAScript Proposal: github.com/tc39/proposal-observable Another spec implementation: github.com/zenparsing/zen-observable
THANKS! Jeremy Fairbank blog.jeremyfairbank.com @elpapapollo | gh/jfairbank bit.ly/devnexus-rise-of-async SLIDES: github.com/jfairbank/rise-of-async-js-talk
CODE: