Async/Await

JaxNode July 2021

About Me

fek.io/blog

youtube.com/c/polyglotengineer

github.com/davidfekke

@jaxnode @polyglotengine1

Async/Await

  • Asynchronous Programming
  • These are two magic keywords
  • Being added to more languages
  • Just added to Rust
  • Being Added to Swift

Concurrency is Hard! 

Concurrency should be as easy as riding a bicycle

Why is concurrency important?

AMD Ryzen has 32 Cores

State of Computing today

  • Processors aren't getting faster
  • Still adding more transistors to piece of silicon 
  • More cores being added to processors
  • Speed is coming from being able to do many things concurrently
  • Windows 7 any method longer than 40 ms must have asynchronous version
setTimeout(function() {
    console.log('World!');
}, 2000);

console.log('Hello ');

Node and Async

  • Node.js is Single Threaded
  • JavaScript had to be asynchronous
  • Netscape Navigator was released when there was no pre-emptive multi-tasking in modern OSs
  • Use LibUV under the hood
  • Error first Callbacks
  • Promises

* https://www.codementor.io/theresamostert/understanding-non-blocking-i-o-in-javascript-cvmg1hp6l

Error First Callbacks

  • Instead of returning a value, functions will expect parameter takes a another function 
  • The function or lambda will contain an error parameter, and a results parameter
  • function query('Arg', function (err, data) {...})

Error first callbacks

function callFn(err, result) {
    if (err) console.error(err);
    console.log(result);
}

function makeDatabaseQuery(params, cb) {
    // ...Do your magic
    const result = magicIsCompleted();
    if (error) {
        cb(error, null);
    } else {
        cb(null, result);
    }
}

makeDatabaseQuery(['12', false, 'Larry'], callFn);

Callback Hell

  • JavaScript allows inline functions or lambda
  • Developers can wind up having massive trees of callbacks inside one method
  • Hard to read and unintuitive
fs.readdir(source, function (err, files) {
  if (err) {
    console.log('Error finding files: ' + err)
  } else {
    files.forEach(function (filename, fileIndex) {
      console.log(filename)
      gm(source + filename).size(function (err, values) {
        if (err) {
          console.log('Error identifying file size: ' + err)
        } else {
          console.log(filename + ' : ' + values)
          aspect = (values.width / values.height)
          widths.forEach(function (width, widthIndex) {
            height = Math.round(width / aspect)
            console.log('resizing ' + filename + 'to ' + height + 'x' + height)
            this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
              if (err) console.log('Error writing file: ' + err)
            })
          }.bind(this))
        }
      })
    })
  }
})

Promises

  • A type of Monad
  • Built in type in Node and JavaScript
  • Can chain and nest Promises
  • Handling functions with .then and .catch
  • Avoids callback Hell, but can still be tricky
const promise = new Promise(function(resolve, reject) {
  // do a thing, possibly async, then…

  if (/* everything turned out fine */) {
    resolve("Stuff worked!");
  }
  else {
    reject(Error("It broke"));
  }
});

promise.then(result => {
    console.log(result);
}).catch(err => {
    console.error(err);
});
const promise = new Promise(function(resolve, reject) {
  resolve(1);
});

promise.then(function(val) {
  console.log(val); // 1
  return val + 2;
}).then(function(val) {
  console.log(val); // 3
})

Async Await Keywords

  • 'async/await' added Node and JavaScript in Node v8 
  • To make any function asynchronous, place 'async' keyword to the beginning of function
  • Place 'await' keyword in front of asynchronous calls like promises instead of using '.then()' handler 
  • Allows JavaScript developers to write their code in a more synchronous way
  • Also available in C#
  • In Swift 5.5, iOS/iPadOS 15, MacOS 12
  • Also in Rust

Async/Await behind the scenes

  • Async tells the compiler to expect awaits on function
  • Await proceeds Promises, but allow synchronous style syntax
  • Compiler will divide functions into starting and callback functions
  • If you have one 'await' keyword function will be divided into two functions 
function resolveAfter2Seconds() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('resolved');
    }, 2000);
  });
}

async function asyncCall() {
  console.log('calling');
  const result = await resolveAfter2Seconds();
  console.log(result);
  // expected output: 'resolved'
}

asyncCall();
function resolveAfter2Seconds() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('resolved');
    }, 2000);
  });
}

console.log('calling');
const result = await resolveAfter2Seconds();
console.log(result);

Top level await using type Module

Considerations

  • Design your APIs to use Promises
  • async functions and Promises are interchangeable
  • Node process still Single-Threaded
  • Making function async does not make it faster

Other Languages that feature Async/Await

  • F#
  • C#
  • Rust
  • Swift
open System
open System.IO

let printTotalFileBytes path =
    async {
        let! bytes = File.ReadAllBytesAsync(path) |> Async.AwaitTask
        let fileName = Path.GetFileName(path)
        printfn $"File {fileName} has %d{bytes.Length} bytes"
    }

[<EntryPoint>]
let main argv =
    printTotalFileBytes "path-to-file.txt"
    |> Async.RunSynchronously

    Console.Read() |> ignore
    0

F#

private static async Task<int> DownloadDocsMainPageAsync()
{
   Console.WriteLine($"{nameof(DownloadDocsMainPageAsync)}: About to start downloading.");

   var client = new HttpClient();
   byte[] content = await client.GetByteArrayAsync("https://docs.microsoft.com/en-us/");

   Console.WriteLine($"{nameof(DownloadDocsMainPageAsync)}: Finished downloading.");
   return content.Length;
}

C#

async fn example(min_len: usize) -> String {
    let content = async_read_file("mycatdata.txt").await;
    if content.len() < min_len {
        content + &async_read_file("myfelinedata.txt").await
    } else {
        content
    }
}

Rust

func processImageData1() async -> Image {
    let dataResource  = await loadWebResource("dataprofile.txt")
    let imageResource = await loadWebResource("imagedata.dat")
    let imageTmp      = await decodeImage(dataResource, imageResource)
    let imageResult   = await dewarpAndCleanupImage(imageTmp)
    return imageResult
}

Swift async/await

async let firstPhoto = downloadPhoto(named: photoNames[0])
async let secondPhoto = downloadPhoto(named: photoNames[1])
async let thirdPhoto = downloadPhoto(named: photoNames[2])

let photos = await [firstPhoto, secondPhoto, thirdPhoto]
show(photos)

Swyft Spawn multiple Threads

Demo

Questions?

Async/Await

By David Fekke

Async/Await

  • 684