Asynchronous in JS
Why does it matter?

JS is monothreaded, everywhere

The olympics of async!
The event loop - Question 1
function f() {
console.log("foo");
setTimeout(g, 0);
console.log("baz");
h();
}
function g() {
console.log("bar");
}
function h() {
console.log("blix");
}
f();
A) foo, bar, baz, blix
B) foo, baz, blix, baz
C) foo, baz, bar, blix
D) Answer D
The event loop - Answer 1
function f() {
console.log("foo");
setTimeout(g, 0);
console.log("baz");
h();
}
function g() {
console.log("bar");
}
function h() {
console.log("blix");
}
f();
- foo
- baz
- blix
- bar
foo, bar, baz, blix
foo, baz, blix, baz
foo, baz, bar, blix
Answer D
The event loop - Question 2
function f() {
let i = null;
for (i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i)
}, 0);
console.log(i);
}
}
f();
A) 0, 0, 1, 1, 2, 2
B) 0, 1, 2, 3, 3, 3
C) 0, 1, 2, 0, 1, 2
D) Other / It depends
The event loop - Answer 2
function f() {
let i = null;
for (i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i)
}, 0);
console.log(i);
}
}
f();
0, 0, 1, 1, 2, 2
0, 1, 2, 3, 3, 3
0, 1, 2, 0, 1, 2
Other / It depends
The event loop

Async functions
Callbacks
- Easy to write and understand...
- ... when you only have a couple
- Callback hell!
- Hard to test

Callbacks - example
toggleCall = () => {
this.upsideDown.enter(() => {
this.setState({ strangerStatus: 'In upside down' });
this.upsideDown.findDemogorgon(location => {
this.setState({ strangerStatus: `Demogorgon in ${location}` });
this.upsideDown.killDemogorgon(result => {
if (result === 'SUCCESS') {
this.setState({ strangerStatus: 'Demogorgon dead ️☠️' });
return;
}
this.setState({ strangerStatus: 'You are dead ☠️' });
});
});
});
};
Promise
- Easier to chain
- Easier to manage errors
- Can be used natively in browser & node
- Not really easy to test
- Confusing at first
Promise - Question 3
Promise.resolve('foo')
.then(Promise.resolve('bar'))
.then(result => {
console.log(result);
});
A) foo
B) bar
C) null
D) throws an error
Promise - Answer 3
Promise.resolve('foo')
.then(Promise.resolve('bar'))
.then(result => {
console.log(result);
});
Promise.resolve('foo')
.then(result => {
return Promise.resolve('bar')
})
.then(result => {
console.log(result);
});
foo
bar
null
It throws an error
Promise - Example
toggleCall = () => {
return this.upsideDownPromise
.enter()
.then(() => {
this.setState({ strangerStatus: 'In upside down' });
return this.upsideDownPromise.findDemogorgon();
})
.then(location => {
this.setState({ strangerStatus: `Demogorgon in ${location}` });
return this.upsideDownPromise.killDemogorgon();
})
.then(result => {
if (result === 'SUCCESS') {
this.setState({ strangerStatus: 'Demogorgon dead ️☠️' });
return;
}
this.setState({ strangerStatus: 'You are dead ☠️' });
});
};
Async/await - Example
toggleCall = async () => {
await this.upsideDownPromise.enter();
this.setState({ strangerStatus: 'In upside down' });
const location = await this.upsideDownPromise.findDemogorgon();
this.setState({ strangerStatus: `Demogorgon in ${location}` });
const result = await this.upsideDownPromise.killDemogorgon();
if (result === 'SUCCESS') {
this.setState({ strangerStatus: 'Demogorgon dead ️☠️' });
return;
}
this.setState({ strangerStatus: 'You are dead ☠️' });
};
Saga - Example
export function* toggleCall(): Generator<*, *, *> {
yield call([upsideDown, upsideDown.enter]);
yield put(updateStatus('In under world'));
const place = yield call([upsideDown, upsideDown.findDemogorgon]);
yield put(updateStatus(`Found demogorgon in ${place}`));
const result = yield call([upsideDown, upsideDown.killDemogorgon]);
if (result === 'SUCCESS') {
yield put(updateStatus(`Demogorgon is dead!`));
} else {
yield put(updateStatus(`You are dead`));
}
}
MobX Observable
export default class UpsideDownStore {
upsideDown = new UpsideDownPromise();
status = observable('In real world');
toggleCall = action(async () => {
await this.upsideDown.enter();
runInAction(() => {
this.status.set('In under world');
});
const place = await this.upsideDown.findDemogorgon();
runInAction(() => {
this.status.set(`Found demogorgon in ${place}`);
});
const result = await this.upsideDown.killDemogorgon();
runInAction(() => {
if (result === 'SUCCESS') {
this.status.set('Demogorgon is dead!');
} else {
this.status.set('You are dead');
}
});
});
}
VueX
import * as types from './mutation-types'
export const toggleAction = async ({ commit, state }) => {
await state.upsideDownPromise.enter()
commit(types.UPDATE_STATUS, 'In upside down')
const location = await state.upsideDownPromise.findDemogorgon()
commit(types.UPDATE_STATUS, `Demogorgon in ${location}`)
const result = await state.upsideDownPromise.killDemogorgon()
if (result === 'SUCCESS') {
commit(types.UPDATE_STATUS, 'Demogorgon dead ️☠️')
return
}
commit(types.UPDATE_STATUS, 'You are dead ☠️')
}
<template>
<ButtonWithStatus
:toggle-call="toggleAction"
:stranger-status="storedStatus"
button-text="VueX call"
></ButtonWithStatus>
</template>
<script>
import { mapActions, mapState } from 'vuex'
import ButtonWithStatus from './ButtonWithStatus'
export default {
name: 'VuexCall',
components: {
ButtonWithStatus
},
methods: {
...mapActions(['toggleAction'])
},
computed: mapState({
storedStatus: state => state.status
})
}
</script>
One last thing
Mulithread?
How to use all your processor cores?
- Cluster mode PM2
- NapaJS
Links
- General async considerations
- The event loop
- Promises
Async in JS
By Kevin Raynel
Async in JS
- 1,165