Loading

Asynchronous I/O in JavaScript

Jared Anderson

This is a live streamed presentation. You will automatically follow the presenter and see the slide they're currently on.

Javascript Fundamentals

  Asynchronous

I/O

Buzzwords

  • Async
  • Non-Blocking
  • Event Loop
  • Single Process
  • Single Threaded
  • Events
  • Callbacks
  • Promises
  • Async / Await
  • Sync
  • Blocking
  • Race Conditions
  • Callback Hell
  • .then().then().then().then()

Common Noob Mistakes

I CANT EVEN

for (var i = 0; i < 5; i++) {
    setTimeout(function () {
        console.log(i);
    }, i);
}

Let's Race

// PUT THIS AT HIGHER SCOPE
// SO I CAN USE IT LATER
var myData;


// POPULATE `myData`
fs.readFile('/file.json', { encoding: 'utf8' }, function (err, data) {  

  if (err) {
    return console.log('Oops...');
  }

  myData = JSON.parse(data);

});


// DO SOME OTHER STUFF THAT
// TAKES `n` AMOUNT OF TIME


// NOW USE `myData`
// (AND HOPE IT'S READY)
console.log(myData); 

Why Does JS Work This Way?!?!

Async I/O

Wait What?

Let's Pretend

Thread / CPU

I/O

The Blocking USPS

Worst Mail System Ever

Your Neighborhood

Mail Comes

You Grab Mail

zzz

Truck is waiting...

You Read Mail

zzz

Truck is waiting...

You Pay Bills

zzz

Truck is waiting...

Truck Moves On

Neighbor Grabs Mail

zzz

Truck is waiting...

Neighbor Reads Mail

zzz

Truck is waiting...

Neighbor Pays Bills

zzz

Truck is waiting...

Truck Goes to Next House...

& so on & so forth

What's Wrong?

  • mail system is inefficient
  • mail truck spends most it's time waiting
  • nothing else happens during I/O
  • people are slow (or not home)

The Threaded USPS

Sledgehammer vs Fly

Your Neighborhood

Trucks Pull Up

#1

#2

Everyone Grabs Mail...

#1

#2

Trucks are waiting...

Everyone Reads Mail...

#1

#2

Trucks are waiting...

Everyone Pays Bills

#1

#2

Trucks are waiting...

Etc

What's Wrong?

  • one truck per person?
  • trucks aren't free
  • trucks still spend most their time idle
  • not best use of trucks
  • people are the bottleneck

The Async USPS

The IRL USPS

Your Neighborhood

Truck Drops off Mail

Truck Moves on to Neighbor

Truck Just Keeps on Truckin'

Time Passes

People Get Mail

People Read Mail

Some Pay Bills

puts up flag

Truck returns the next day...

gives and receives new instructions

What's Right?

  • truck not limited to speed of humans
  • best use of truck resources
  • people can procrastinate

CPU & IO work in Async!

Async I/O?

In computer science, asynchronous I/O, or non-blocking I/O is a form of input/output processing that permits other processing to continue before the transmission has finished.

like our neighbors

allowing the truck move on

Start the action and then wait for it to complete. Such an approach would block the progress of a program while the communication is in progress, leaving system resources idle

Opposite of Async?

zzz

When a program makes many I/O operations, this means that the processor can spend almost all of its time idle waiting for I/O operations to complete.

Sync / Blocking

I/O operations on a computer can be extremely slow compared to the processing of data. 

Sync / Blocking

 I/O device can incorporate mechanical devices that must physically move, such as a hard drive seeking a track to read or write; this is often orders of magnitude slower than the switching of electric current.

YUNO FAST?

during a disk operation that takes ten milliseconds to perform, a processor that is clocked at one gigahertz could have performed ten million instruction-processing cycles.

For Example

and I/O often includes remote network request

Thus

CPU is pretty much never the bottleneck

for web-apps

I/O is the Bottleneck

Truck Route == Event Loop

event loop much faster ;-)

Event Loop

https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop

Event Loop

  • You, as a web app developer, don't really need to worry about it
  • A high level understanding is sufficient.
  • You deal with it indirectly

OK, How do I make my I/O operations Asynchronous?

You don't*. Async operations are built into the environment. You just leverage them.

In the Browser

  • XHR / Fetch
  • IndexDB
  • CSS3 Animations
  • SQLite
  • Workers
  • GeoLocation

In the Nodez

Core Modules

  • fs
  • http
  • net
  • more..

Addons*

  • C/C++ modules (vendor db drivers, etc)

OK, How Do I Async?

Easy:

just don't block main process

On the Nodez

avoid the `___Sync` counterparts of ASYNC operations

Otherwise, Just Interact with Async IO with APIs

  1. Eventswhen X happens, do Y
  2. Callbacks: do X & when done do Y
  3. Promises:  give me promise P that you’ll do X
  4. Async / Await: funkless promises

*Other's too, but these are standards based

Let's Take a Look

Events

  When X happens, do Y

Pros

  • Reactionary
  • Fast
  • Decoupled

Cons

  • Event must first be triggered -- triggering can be verbose & not reusable
  • Orchestration of Async events is hard, verbose, and not reusable.

Events

var request = new XMLHttpRequest();
request.open('GET', '/my/api', true);

request.onload = function() {
  if (request.status >= 200 && request.status < 400) {
    // Success!
    var data = JSON.parse(request.responseText);
  } else {
    // We reached our target server, but it returned an error

  }
};

request.onerror = function() {
  // There was a connection error of some sort
};

request.send();

Example

Callbacks

  Do X & when done do Y

Pros

  • Triggering code is more reusable
  • Still fast
  • Decoupled

Cons

  • Orchestration of Async Events leads to Callback Hell

Callbacks

Browser

Geolocation

Wrapper around Events

Nodez

Most of the Core APIs

Where are Callbacks?

window.addEventListener('click', e=>console.log(e));

Browser Example

navigator.geolocation.getCurrentPosition(doSomethingWithPosition);


function doSomethingWithPosition(position) {
    /* position is an object like
    position.coords.latitude,
    position.coords.longitude
    */
    console.log(position);
}

Browser Example

Node Example 

var fs = require('fs');

fs.readFile('/file.json', { encoding: 'utf8' }, function (err, data) {  

  if (err) {
    return console.log('Oops...');
  }

  // do something with `JSON.parse(data)`


});

Exercise 

var request = new XMLHttpRequest();
request.open('GET', '/my/api', true);

request.onload = function() {
  if (request.status >= 200 && request.status < 400) {
    // Success!
    var data = JSON.parse(request.responseText);
  } else {
    // We reached our target server, but it returned an error

  }
};

request.onerror = function() {
  // There was a connection error of some sort
};

request.send();

Turn non-reusable, XHR code...

Exercise 

requestJSON('/my/api', function(err, data) {

    if(err) {
        // do something with error
    }

    // do something with data...

});

...into reusable API such as:

function requestJSON(url, cb) {

    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    
    request.onload = function() {
      if (request.status >= 200 && request.status < 400) {
        // Success!
        var data = JSON.parse(request.responseText);
        cb(null, data);
      } else {
        // We reached our target server, but it returned an error
        cb("Error");
      }
    };
    
    request.onerror = function() {
        cb("Error");
      // There was a connection error of some sort
    };
    
    request.send();

}

Exercise Answer

Promises

  Give me promise P that you’ll do X

Pros

  • Responsibility of what to do next is shifted
  • Future values can be passed around as values
  • Chaining Much Easier
  • Orchestration of Async events much easier

Cons

  • Can swallow errors
  • Funky API

Promises

Browser

  • Fetch
  • Wrapper around Event or Callback

Nodez

  • Wrapper around Events or Callback
  • 3rd-party modules

Where are Promises?

function getJSON(url) {
    return fetch(url)
        .then(ensureOK)
        .then(toJSON)
        .catch(handleAPIError);
    
    function ensureOK(response) {
        if(!response.ok) {
            return Promise.reject(response);
        }
        return response;
    }
    
    function toJSON(response) {
        return response.json();
    }
    
    function handleAPIError(response) {
        // log and punt error
        console.error('API Error Happened');
        return Promise.reject(response);
    }
}

Browser Example

Exercise

Exercise Answer

function getJSON(url) {
  
    return new Promise(function(resolve, reject){
        var request = new XMLHttpRequest();
        request.open('GET', url, true);

        request.onload = function() {
            if (request.status >= 200 && request.status < 400) {
                resolve(JSON.parse(request.responseText));
            } else {
                reject(request.status);
            }
        };
    
        request.onerror = function() {
          reject("Baaad")

        };

        request.send();

    });
  
}

Async / Await

++Promises

Pros

  • Concise code
  • Write code just like sync
  • Better syntax to promises

Cons

  • ES7: must transpile AND polyfill
  • No inner async functions
  • try / catch not optimized on the Nodez & not roadmapped either

Async / Await

async function requestJSON(url) {
    try {
        let response = await fetch(url);        

        if(!response.ok) {
          throw "API Error Happened";
        }        

        return response.json();
    } catch(e) {
      console.error(e);
      throw e;
    }
}

Browser Example

(async () => {
  try {
    let data = await requestJSON('/my/api');
    console.log(data)
  } catch(e) {
    console.error(e);  
  }
})();

Browser Example

Solutions