async/await

The [a]wait is over!

About Me

Warren Seymour

 

Owner, Fountainhead Technologies Ltd.

 

warren@fountainhead.tech

Summary

  1. Asynchronicity - Overview & History
  2. 'await' and 'async' to the rescue!
  3. Cool Stuff!
  4. Native & Transpiler Support
  5. 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();
  1. Prefix call with await
    • Promise is 'unwrapped'
  2. Value is no longer a Promise
    • It may be assigned directly to a variable

async

function doTwoThings() {
  return doOneThing()
    .then(resultOne => {
      return doAnotherThing(resultOne);
    });
}
  1. Convert Promise.then(s) to await
  2. Add async prefix
  3. '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