Async Functions
Awaiting You

Tomasz Ducin

Tomasz Ducin

19th June 2017, Warsaw

async functions

awaiting you

Tomasz Ducin

25th May 2017, Warsaw

Async Functions
Awaiting You

Tomasz Ducin

JavaScript, Java, Python

software dev @ Ivanti

trainer @ Bottega IT Minds

functions

sync vs async

promises

async await

coroutines

CSP

generators

callbacks

events

reactive streams

run to completion

event loop

a Promise of
a single operation
to be completed in future

fetchData()
.then(callbackFn)
.catch(errorHandlerFn)
myCustomOperation()
.then(callbackFn)
.catch(errorHandlerFn)

Promise - states

pending

fulfilled

rejected

initial state,
not settled yet

operation has failed

operation has completed successfully

settled

promise limitations

  • single item

  • one-time item

  • greedy

  • not cancellable

  • values unavailable outside the chain

  • only previous step value available


  return asyncOp1();
function promiseChain(){
  return asyncOp1()
    .then(asyncOp2)
    .then(asyncOp3)
    .then(asyncOp4);
}

  return asyncOp1()
    .then(asyncOp2);

  return asyncOp1()
    .then(asyncOp2)
    .then(asyncOp3);

  return asyncOp1()
    .then(asyncOp2)
    .then(asyncOp3)
    .then(asyncOp4);
function sequential(){
  return asyncOp1()
    .then(asyncOp2)
    .then(asyncOp3)
    .then(asyncOp4);
}

Sequential Processing

op1
op2
op3
op4
sequential()
  .then(anotherAsync)
another
function parallel(){
  var p1 = asyncOp1();
  var p2 = asyncOp2();
  var p3 = asyncOp3();
  var p4 = asyncOp4();
  return Promise.all(
    [p1, p2, p3, p4]);
}

Concurrent Processing

op1
another
.all
parallel()
  .then(anotherAsync)
op2
op3
op4
Promise.all([p1, p2, p3, p4])
  .then( ([v1, v2, v3, v4]) => {...})
  .catch( reason => {...})

Promise Aggregates

Promise.race([p1, p2, p3, p4])
  .then( value => {...})
  .catch( reason => {...})
Promise.any([p1, p2, p3, p4])
  .then( value => {...})
  .catch( ([r1, r2, r3, r4]) => {...})
all
race
any
Promise.some([p1, p2, p3, p4], 2)
  .then( (v1, v2) => {...})
  .catch( ([r1, r2, r3]) => {...})
some

didn't mention

  • chaining, splitting, joining

  • error handling

  • antipatterns
    (broken chain, deferred)

  • jQuery 1.x, 2.x promise broken

  • referring to previous steps

function zeroOneTwo(){
  return [0, 1, 2];
}
function* zeroOneTwo(){
  yield 0;
  yield 1;
  yield 2;
}

lazy

greedy

list

generator

synchronous

synchronous

for (var i of zeroOneTwo()) {
  console.log(i);
} // 1 2 3
function* generator(){
  console.log(1, "inside");
  yield "A";
  console.log(2, "inside");
  yield "B";
}

var iterator = generator();
console.log(1, "outside");
iterator.next();
console.log(2, "outside");
iterator.next();
console.log(3, "outside");

synchronous

Generators

vs run to completion

1: outside
1: inside
2: outside
2: inside
3: outside
function* generate(){
  console.log(1, "inside");
  let recv = yield "A";
  console.log(2, "inside", recv);
  yield "B";
}

var iter = generate();
console.log(1, "outside");
let item = iter.next();
console.log(2, "outside", item.value);
iter.next('hey!');
console.log(3, "outside");

synchronous

Generators

vs run to completion

1: outside
1: inside
2: outside, A
2: inside, hey!
3: outside

= coroutines

promises + generators

+ 1 tiny wrapper

  • start immediately
  • suspend on promise
     
  • resume when settled
  • promise pending...
  • resolve/ reject calls next

writing asynchronous code in synchronous manner

function* sequential(){
  var v1 = yield asyncOp1();
  var v2 = yield asyncOp2(v1);
  var v3 = yield asyncOp3(v2);
  return asyncOp4(v3);
}
let asyncSequential
  = async(sequential);

Sequential Processing

asyncSequential()
  .then(anotherAsync)
op1
op2
op3
op4
another
function* parallel(){
  var p1 = asyncOp1();
  var p2 = asyncOp2();
  var p3 = asyncOp3();
  var p4 = asyncOp4();
  return yield p1 + yield p2
    + yield p3 + yield p4;
}
let asyncParallel
  = async(parallel)
asyncParallel()
  .then(anotherAsync)
op1
another
all yield
op2
op3
op4

Concurrent Processing

function async(makeGenerator){
  return function () {
    var generator = makeGenerator.apply(this, arguments);

    function handle(result){
      // result => { done: [Boolean], value: [Object] }
      if (result.done) return Promise.resolve(result.value);
      return Promise.resolve(result.value).then(function (res){
        return handle(generator.next(res));
      }, function (err){
        return handle(generator.throw(err));
      });
    }

    try {
      return handle(generator.next());
    } catch (ex) {
      return Promise.reject(ex);
    }
  }
}

a couple of lines that
do the right thing...

ES2017 / ES8

async function sequential(){
  var v1 = await asyncOp1();
  var v2 = await asyncOp2(v1);
  var v3 = await asyncOp3(v2);
  return asyncOp4(v3);
}

Sequential Processing

sequential()
  .then(anotherAsync)
op1
op2
op3
op4
another
async function parallel(){
  var p1 = asyncOp1();
  var p2 = asyncOp2();
  var p3 = asyncOp3();
  var p4 = asyncOp4();
  return await p1 + await p2
    + await p3 + await p4;
}
parallel()
  .then(anotherAsync)
op1
another
all await
op2
op3
op4

Concurrent Processing

Pipeline scope

function promiseChain(){
  return asyncOp1()
    .then(asyncOp2)
    .then(asyncOp3)
    .then(asyncOp4 ???);
}

using data from previous steps

async function sequential(){
  var v1 = await asyncOp1();
  var v2 = await asyncOp2(v1);
  var v3 = await asyncOp3(v2);
  return asyncOp4(v1, v2, v3);
}

No nested functions

same as in generators

async function renderChapters(urls) {
  urls.map(getJSON)
    .forEach(p => addToPage((await p).html));
}
async function renderChapters(urls) {
  urls.map(getJSON)
    .forEach(async p => addToPage((await p).html));
}
            Syntax Error
        
            parallel
        

Async Arrow Functions

(async x => x ** 2);
(async x => { return x ** 2; });
(async (x, y) => x ** y);
(async (x, y) => { return x ** y; });

instead of Promise.resolve

const square = (async x => x ** 2);
square(5) // same as Promise.resolve(25)

square(5).then(console.log)
// output: 25

Async Iteration

for await (const line of readLines(filePath)) {
  console.log(line);
}
syncIterator.next()
// -> { value: ..., done: ... }
asyncIterator.next()
// -> Promise resolving with
//    { value: ..., done: ... }
.then(({ value, done }) => /* ... */);

lazy and asynchronous

Top-level await

// all awaited functions return a promise
// all awaited functions return a promise

await delay(2000);
// all awaited functions return a promise

await delay(2000);

// webservices
const flowers = await fetch('flowers.jpg');
// all awaited functions return a promise

await delay(2000);

// webservices
const flowers = await fetch('flowers.jpg');

// I/O operations
let content = '...';
let fileName = 'filename.json';
await writeFile(fileName, content);
await doSomeProcessing(fileName);

Rich Harris, creator of Rollup

var data = await something();
// import gets blocked
const m = import 'module';

m.run();
if (!Array.prototype.myMethod){
  ...
  await something();
}
var data = await something();
export default data;

module.js

Top-level await

consumer.js

THX!