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
- Events: when X happens, do Y
- Callbacks: do X & when done do Y
- Promises: give me promise P that you’ll do X
- 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
Asynchronous I/O in JavaScript
By Jared Anderson
Asynchronous I/O in JavaScript
An introduction to Asynchronous I/O in Javascript. Understanding how the event loop works, then looking at interacting with async I/O using events, callbacks, promises, async/await, etc.
- 2,142