A fairly succinct journey through the many ways javascript developers have learned to cope with asynchrony. Callbacks vs Promises vs RxJS vs async/await link
// The problem const getUser = () => { setTimeout(() => { return {name: 'Max'} }, 2000); }; const user = getUser(); // user is null console.log(user.name); // so this won't work
// The callback const getUser = (cb) => { setTimeout(() => { cb({name: 'Max'}); }, 2000); }; getUser((user) => { console.log(user.name); // Prints 'Max' after 2 seconds })
// callback hell const checkAuth = (cb) => { // timer simulates network latency setTimeout(() => { cb({isAuth: true}); }, 2000); }; const getUser = (authInfo, cb) => { if (!authInfo.isAuth) { cb(null); return; } setTimeout(() => { cb({name: 'Max'}); }, 2000); }; // this next part get much worse in Real Life checkAuth((authInfo) => { getUser(authInfo, (user) => { console.log(user.name); }); });
// The promise const getUser = () => { return new Promise(resolve => { setTimeout(() => { resolve({name: 'Max'}); }, 2000); }); }; getUser() .then(user => { console.log(user.name); })
// Promised relief from callback hell checkAuth() .then(authStatus => getUser(authStatus)) .then(user => console.log(user.name)) .catch(error => { // handle error here });
// Promises only resolve once const button = document.querySelector('button'); const handleClick = () => { return new Promise(resolve => { button.addEventListener('click', () => { resolve(event); }); }); }; // So this doesn't do what you mean handleClick().then(event => { console.log(event.target); });
_N.B. the RxJS examples are apparently out-of-date_
// RxJS Observables handle event streams const button = document.querySelector('button'); const observable = Rx.Observable.fromEvent(button, 'click'); observable.subscribe( event => console.log(event.target), error => console.log(error) );
// RxJS switchMap operator to chain observables const button = document.querySelector('button'); Rx.Observable.fromEvent(button, 'click') .switchMap(event => Rx.Observable.timer(1000)) .subscribe( (data) => console.log(data) );
// RxJS offers a menagerie of stream operators // maybe even convenient enough for non-async code const observable = Rx.Observable.of({name: 'Max'}); observable .pluck('name') .subscribe(console.log);
// async/await also promising ;-) async function fetchUser() { try { const auth = await checkAuth(); const user = await getUser(auth); return user; } catch (error) { return {name: 'Default'}; } } fetchUser().then(user => console.log(user.name));
.
// more modern RxJS example -- pipe! const { fromEvent } = rxjs; const { throttleTime, scan } = rxjs.operators; const button = document.querySelector('button'); fromEvent(button, 'click').pipe( throttleTime(5000), scan(count => count + 1, 0) ) .subscribe(count => console.log(`Clicked ${count} times`));
Apparently generalizing over that progression of asynchrony: Why we need callbags link
Mostly tangential comment caught my eye _(emphasis added)_: > the long-term goal of Cycle.js : to be a **framework for visual programming**
André Staltz also cryptographicly signs his articles: > You can make sure that the author wrote this post by copy-pasting this signature into this Keybase page .
Once upon a time, I observed event-based computing in GUIs was showing up server-side: blog . I also claimed there that pipeline pattern and state pattern were duals. Do I finally live in a world where these reactive event streams flow from business analysis through the network all the way through the UI? Event Storming