$30 off During Our Annual Pro Sale. View Details »
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.3k
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
220
CodeMash 2020: Solving the Boolean Identity Crisis
jfairbank
1
170
CodeMash 2020: Practical Functional Programming
jfairbank
1
340
Connect.Tech 2019: Practical Functional Programming
jfairbank
0
380
Connect.Tech 2019: Solving the Boolean Identity Crisis
jfairbank
0
190
Lambda Squared 2019: Solving the Boolean Identity Crisis
jfairbank
0
140
All Things Open 2018: Practical Functional Programming
jfairbank
2
270
Connect.Tech 2018: Effective React Testing
jfairbank
1
180
Fluent Conf 2018: Building web apps with Elm Tutorial
jfairbank
2
880
Other Decks in Programming
See All in Programming
俺流レスポンシブコーディング 2025
tak_dcxi
14
8.9k
堅牢なフロントエンドテスト基盤を構築するために行った取り組み
shogo4131
8
2.4k
モデル駆動設計をやってみようワークショップ開催報告(Modeling Forum2025) / model driven design workshop report
haru860
0
270
re:Invent 2025 のイケてるサービスを紹介する
maroon1st
0
120
251126 TestState APIってなんだっけ?Step Functionsテストどう変わる?
east_takumi
0
320
AIの誤りが許されない業務システムにおいて“信頼されるAI” を目指す / building-trusted-ai-systems
yuya4
6
3.7k
まだ間に合う!Claude Code元年をふりかえる
nogu66
5
840
ゲームの物理 剛体編
fadis
0
350
從冷知識到漏洞,你不懂的 Web,駭客懂 - Huli @ WebConf Taiwan 2025
aszx87410
2
2.7k
関数実行の裏側では何が起きているのか?
minop1205
1
700
【CA.ai #3】Google ADKを活用したAI Agent開発と運用知見
harappa80
0
320
AIエージェントを活かすPM術 AI駆動開発の現場から
gyuta
0
430
Featured
See All Featured
jQuery: Nuts, Bolts and Bling
dougneiner
65
8.3k
Raft: Consensus for Rubyists
vanstee
141
7.2k
Making Projects Easy
brettharned
120
6.5k
Speed Design
sergeychernyshev
33
1.4k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.3k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.6k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
1.8k
Building Adaptive Systems
keathley
44
2.9k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
Fashionably flexible responsive web design (full day workshop)
malarkey
407
66k
4 Signs Your Business is Dying
shpigford
186
22k
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: