async/await
The [a]wait is over!
About Me
Warren Seymour
Owner, Fountainhead Technologies Ltd.
warren@fountainhead.tech
Summary
- Asynchronicity - Overview & History
- 'await' and 'async' to the rescue!
- Cool Stuff!
- Native & Transpiler Support
- Questions
Latency Visualised
1 CPU Instruction
0.3ns
Level 1 Cache Access
1ns
Level 2 Cache Access
3ns
Level 3 Cache Access
13ns
Main Memory Access
100ns
1,000ns = 1us
Read 1MB from Memory
9us
SSD Random Access
13us
Read 1MB From SSD
200us
1,000us = 1ms
(1,000,000 ns)
Ping Response
10ms
Read 1MB at 30Mb/s
300ms
waiting...
... still waiting ...
...finished!
In The Beginning
CS invented Threads
"Theads were hard and boring, darkness was over the asynchronous I/O."
Let There be Callbacks
"CS saw that the callbacks were good, and separated async I/O from the darkness."
doThing(function(err, result) {
if (err) {
console.error('oops', err);
return;
}
console.log('Here is the result', result);
});
console.log(
'I can carry on doing stuff while we wait...'
);
doThing(function(err, result) {
doAnotherThing(result, function(err, result) {
console.log('final result is:', result);
});
});
var result1;
var result2;
function maybeDone() {
if (!result1 || !result2) {
return;
}
console.log('here are both results',
result1,
result2
);
}
doThing1(function(err, result) {
result1 = result;
maybeDone();
});
doThing2(function(err, result) {
result2 = result;
maybeDone();
});
[spaghetti intensifies]
Let There be Promises
"So CS made the Promises to separate spaghetti from async I/O."
doSomething()
.then(result => {
return doAnotherThing(result);
})
.then(result => {
console.log('the final result is', result);
});
catch(err => {
console.error('oh no', err);
});
Promise.all([
doSomething(),
doAnotherThing()
]).then(results => {
console.log('the results are',
results[0],
results[1]
);
});
Promises are infectious!
const CONFIG = {
colour: '#FF0000',
format: 'MMM Do YY'
};
function loadConfig() {
return CONFIG;
}
function formatTime() {
const config = loadConfig();
return moment().format(config.format);
}
function colourizeString(message) {
const config = loadConfig();
return chalk.rgb(config.colour, message);
}
console.log(colourizeString(formatTime()));
function loadConfig() {
return loadFileAsync('config.json')
.then(JSON.parse);
}
function formatTime() {
return loadConfig()
.then(config => {
return moment().format(config.format);
});
}
function colourizeString(message) {
return loadConfig()
.then(config => {
return chalk.rgb(config.colour, message);
});
}
formatTime()
.then(colourizeString)
.then(result => console.log(result));
const loadFileAsync = promisify(readFile);
Enter async/await
- Not a replacement for Promises
- Makes Promises Great Again
- await
- Pauses function while waiting for a Promise
- May only be used inside an async function
- The 'caller'
- async
- Allows a function to use await keyword
- Wraps the return value in a Promise
- The 'callee'
await
const promise = doSomething();
promise.then(value => {
...
});
const promise = await doSomething();
promise.then(value => {
...
});
const value = await doSomething();
- Prefix call with await
- Promise is 'unwrapped'
- Value is no longer a Promise
- It may be assigned directly to a variable
async
function doTwoThings() {
return doOneThing()
.then(resultOne => {
return doAnotherThing(resultOne);
});
}
- Convert Promise.then(s) to await
- Add async prefix
- 'Fat Arrow' function usage
function doTwoThings() {
const resultOne = await doOneThing();
const resultTwo = await doAnotherThing(resultOne);
return resultTwo;
}
async function doTwoThings() {
const resultOne = await doOneThing();
const resultTwo = await doAnotherThing(resultOne);
return resultTwo;
}
const doTwoThings = async () => {
const resultOne = await doOneThing();
const resultTwo = await doAnotherThing(resultOne);
return resultTwo;
}
const loadFileAsync = promisify(readFile);
async function loadConfig() {
const config = await loadFileAsync('config.json');
return JSON.parse(config);
}
async function formatTime() {
const config = await loadConfig();
return moment().format(config.format);
}
async function colourizeString(message) {
const config = await loadConfig();
return chalk.rgb(config.colour, message);
}
console.log(await colourizeString(await formatTime()));
const loadFileAsync = promisify(readFile);
async function loadConfig() {
const config = await loadFileAsync('config.json');
return JSON.parse(config);
}
async function formatTime() {
const config = await loadConfig();
return moment().format(config.format);
}
async function colourizeString(message) {
const config = await loadConfig();
return chalk.rgb(config.colour, message);
}
async function main() {
console.log(await colourizeString(await formatTime()));
}
main();
Cool Stuff: Destructuring
async function formatTime() {
const config = await loadConfig();
return moment().format(config.format);
}
async function formatTime() {
const {format} = await loadConfig();
return moment().format(format);
}
Cool Stuff: Infix
async function formatTime() {
const config = await loadConfig();
return moment().format(config.format);
}
async function formatTime() {
return moment().format((await loadConfig()).format);
}
const formatTime = async () =>
moment().format((await loadConfig()).format);
Cool Stuff: Loops (Parallel)
const createWidgets = async (data, count) {
let chain = Promise.resolve();
for(let i = 0; i < count; i++) {
chain = chain
.then(() => httpPost('/api/widgets', data));
}
return chain;
}
Cool Stuff: Loops (Parallel)
const createWidgets = async (data, count) {
for(let i = 0; i < count; i++) {
await httpPost('/api/widgets', data);
}
}
Cool Stuff: Loops (Serial)
{
widgets: [{
name: 'Field Inverter'
}, {
name: 'Core Capacitor'
}, {
...
}],
_links: {
self: '/api/widgets',
next: '/api/widgets?page=2
}
}
GET /api/widgets
Cool Stuff: Loops (Serial)
{
widgets: [{
...
}, {
name: 'Antimatter Container'
}, {
name: 'EPS Relay'
}],
_links: {
self: '/api/widgets?page=2',
prev: '/api/widgets
}
}
GET /api/widgets?page=2
Cool Stuff: Loops (Serial)
const getAllWidgets = () => {
const widgets = [];
const getPage = (pageUrl) =>
httpGet(pageUrl)
.then(response => {
widgets.push(...response.widgets);
if (response._links.next) {
return getPage(response._links.next);
}
});
return getPage('/api/widgets')
.then(() => {
return widgets;
});
}
Cool Stuff: Loops (Serial)
const getAllWidgets = () => {
const widgets = [];
let nextPage = '/api/widgets';
do {
const response = await httpGet(nextPage);
widgets.push(...response.widgets);
nextPage = response._links.next;
}
while (nextPage);
return widgets;
}
Cool Stuff: Parallelism
const widgets = await httpGet('/api/widgets');
const users = await httpGet('/api/users');
doSomethingWithBoth(widgets, users);
const both = await Promise.all([
await httpGet('/api/widgets'),
await httpGet('/api/users')
]);
doSomethingWithBoth(both[0], both[1]);
const [widgets, users] = await Promise.all([
await httpGet('/api/widgets'),
await httpGet('/api/users')
]);
doSomethingWithBoth(widgets, users);
Cool Stuff: Error Handling
try {
const session = await httpGet('/api/session');
initializeApp(session);
} catch (e) {
displayErrorModal({
title: 'Failed to initialize application',
message: 'Could not load session',
code: e.toString()
});
}
Using async/await Today
- Native Support
- Chrome 64 (Jan 2018)
- Edge 16 (Oct 2017)
- Firefox 59 (Mar 2018)
- Safari 11 (Sept 2017)
- Node 8 (Feb 2017)
- Babel
- babel-preset-env
- regenerator-runtime
- TypeScript
- Can emit to ES6, ES5 & ES3!
Questions
Thank You!
async/await
By Warren Seymour
async/await
- 588