Skip to content

Latest commit

 

History

History
684 lines (475 loc) · 9.98 KB

PITCHME.md

File metadata and controls

684 lines (475 loc) · 9.98 KB

async/await workshop

Antonio Valverde


Profile

  • Server side engineer
  • Working at CyberAgent, Inc
  • Currently working with Node.js (ES2017 usage)

Slides URL:

https://goo.gl/Gs93X9

Full URL:

https://gitpitch.com/toniov/async-await-workshop


This workshop can be followed using the Google Chrome console:

  • Windows / Linux: Ctrl + Shift + J
  • Mac: Cmd + Opt + J

The following function will be used in the examples in this workshop:

const delay = (ms) => {
  return new Promise((resolve, reject) => {
    if (isNaN(ms)) {
      return reject(new Error('not a valid number'));
    }
    setTimeout(() => {
      resolve(ms * 2);
    }, ms);
  });
};

+++

This function returns a Promise:

  • Resolved after the specified milliseconds in ms
    • Fulfillment value: ms * 2
  • Rejected if ms ms is not a valid number
    • Rejection value: the error message

+++


Agenda

  • About async/await
  • Async Functions
  • Handling results and errors with await
  • Things you should know
  • Some Pitfalls
  • Quizzes Time

About async/await


async/await in 7 seconds

(Credit: twitter.com/manekinekko


async/await is an ES2017 feature available in:

  • Recent versions of modern browsers (Google Chrome, Firefox, ...)
  • Node.js 8.x >
  • Babel
  • And more

Async Functions


Async function declarations:

async function foo() {}

Async function expressions:

const foo = async function () {};

Async arrow functions:

const foo = async () => {};

Async method definitions:

let obj = { 
  async foo() {}
}

Async functions always return Promises:

async function asyncFunc () {
  return 123;
}

asyncFunc()
.then(x => console.log(x)); // 123

Handling results and errors with await


The operator await

  • Is only allowed inside async functions
  • Waits for its operand, a Promise, to be settled

Is only allowed inside async functions:

function nonAsyncFunc () {
  await Promise.resolve();
}

This is not allowed either:

async function asyncFunc () {
  function nonAsyncFunc() {
    await Promise.resolve();
  }
}

Both will fail with:

Uncaught SyntaxError: Unexpected identifier

Waits for its operand, a Promise, to be settled:

  • Is resolved: the fulfillment value is returned
async function asyncFunc () {
  const result = await delay(100);
  console.log(result); 
  // Output: 200
}

asyncFunc();

+++

  • Is rejected: the rejection value is thrown
async function asyncFunc () {
  try {
    await delay("buh");
  } catch (err) {
    console.error(err.message); 
    // Output: "not a valid number"
  }
}

asyncFunc();

If something besides a Promise is passed to await, it just returns the value as-is:

async function asyncFunc() {
  const result = await 'hola';
  console.log(result);
  // Output: hola
}
asyncFunc();

That means that if an array of Promises is passed, they will not be awaited:

async function asyncFunc() {
  const result = await [delay(100), delay(200)];
  console.log(result);
  // Output: [Promise, Promise]
}
asyncFunc();

Handling multiple asynchronous results sequentially:

async function asyncFunc() {
  const result1 = await delay(100);
  console.log(result1); // 200
  const result2 = await delay(200);
  console.log(result2); // 400
}

Equivalent to:

function asyncFunc() {
  return delay(100)
  .then(result1 => {
    console.log(result1); // 200
    return delay(200);
  })
  .then(result2 => {
    console.log(result2); // 400
  });
}

+++

Handling multiple asynchronous results concurrently:

async function asyncFunc() {
  const [result1, result2] = await Promise.all([
     delay(100),
     delay(200),
  ]);
  console.log(result1, result2); // 200 400
}

Equivalent to:

function asyncFunc() {
  return Promise.all([
    delay(100),
    delay(200),
  ])
  .then([result1, result2] => {
      console.log(result1, result2); // 200 400
  });
}

Handling errors:

async function asyncFunc() {
  try {
    await delay('buh');
  } catch (err) {
    console.error(err);
  }
}

Equivalent to:

function asyncFunc() {
  return delay('buh')
  .catch(err => {
     console.error(err);
  });
}

Things you should know


Async functions are started synchronously, but are settled asynchronously

+++

  1. A Promise p is created when an async function is executed.

  2. Execution of the body starts synchronously.

    1. Execution finish via return or throw (permanently)
    2. Execution finish finish via await (temporarily, it will continue later on)
  3. p is returned

+++

Execution finish via return or throw (permanently):

async function asyncFunc() {
  console.log('inside asyncFunc()');
  return 'abc';
}

asyncFunc()
.then(x => console.log(`Resolved: ${x}`));

console.log('main');

// Output:
// inside asyncFunc()
// main
// Resolved: abc

+++

Execution finish finish via await (temporarily):

async function asyncFunc() {
    console.log('inside asyncFunc()');
    await "dummy";
    console.log('after await')
    return 'abc';
}
asyncFunc()
.then(x => console.log(`Resolved: ${x}`));
console.log('main');

// Output:
// inside asyncFunc()
// main
// after await
// Resolved: abc

Returning Promises

Returned Promises are not wrapped:

async function asyncFunc() {
  return Promise.resolve(123);
}
asyncFunc()
.then(x => console.log(x)) // 123

Returning a rejected Promise leads to the result of the async function being rejected:

async function asyncFunc() {
  return Promise.reject(new Error('Buh'));
}
asyncFunc().catch(err => console.error(err)); 
// Error: Problem!

+++

That allows us to forward fulfillments and rejections of another asynchronous computation without await :

async function asyncFunc() {
  return delay(100);
}

This works as well, but the code above(the one without await) is more efficient:

async function asyncFunc() {
  return await delay(100);
}

Some Pitfalls


One tip:

Remember we are dealing with Promises here


Forgetting await:

async function asyncFunc() {
  const value = delay();
  if (value) {
    console.log(`We have a value`);
  }
}

Bugs can be difficult to detect, like in the code above.


Performance problems:

async function asyncFunc() {
  const value1 = await delay(100);
  const value2 = await delay(200);
  const value3 = await delay(300);
  console.log(`Values: ${value1}, ${value2}, ${value3}`);
}

(This is more of a bad code design problem though...)


Callback-based utilities can be tricky to use:

async function getDelayResults (array) {
  return array.map(ms => {
    const result = await delay(ms);
    return result;
  });
}

The code above will produce a SyntaxError, the innermost function that surrounds an await is not async.

+++

This code works but the result is an array of Promises:

async function getDelayResults (array) {
  return array.map(async ms => {
    const result = await delay(ms);
    return result;
  });
}

getDelayResults([100, 200, 300])
.then(x => console.log(x)); // [Promise, Promise, Promise]

+++

It can be solved using Promise.all():

async function getDelayResults (array) {
  const pArray = array.map(async ms => {
    const result = await delay(ms);
    return result;
  });
  const result = await Promise.all(pArray);
  return result;
}

getDelayResults([100, 200, 300])
.then(x => console.log(x)); // [200, 400, 600]

+++

Or using a for of loop:

async function getDelayResults (array) {
  const result = [];
  for (ms of array) {
    result.push(await delay(ms));
  }
  return result;
}

getDelayResults([100, 200, 300])
.then(x => console.log(x)); // [200, 400, 600]

This runs in series though.

+++

  • Depending on the case the way of solving it is different

  • Solutions that run concurrently might become kind of complex

  • Same problem may occur using Array#forEach(), Array#reduce(), Array#filter(), etc.

+++

Utilities that make iteration easy:

https://github.com/toniov/p-iteration


Quizzes


  1. What will be the output?
async function ping () {
  await delay(300);
  console.log('ping');
}

async function pong () {
  await delay(100);
  console.log('pong');
}

Promise.all([ping(), pong()]);

console.log('main');

+++

+++


  1. What will be the output?
async function ping () {
  console.log('ping');
  await delay(300);
}

async function pong () {
  await delay(100);
  console.log('pong');
}

ping();
pong();

console.log('main');

+++

+++


  1. What will be the output?
async function printDelayResults (array) {
  array.forEach(async ms => {
    const result = await delay(ms);
    console.log(result);
  });
}

printDelayResults([100, 300, 200])
.then(() => {
  console.log('finish');
});

console.log('main');

+++

+++


  1. How can the performance of this function be improved?
async function asyncFunc() {
  const v1 = await delay(300);
  const v2 = await delay(200);  
  const v3 = await delay(100);
  return v1 + v2 + v3;
}

+++

+++


  1. Something is not necessary here
async function asyncFunc() {
  await delay(100);
  return await delay(200);
}

+++

+++


Reference

Exploring ES2016 and ES2017
by Dr. Axel Rauschmayer


Thanks for your attention!

Questions?