Created
November 4, 2015 02:03
-
-
Save CrabDude/75981463cfd3d7605b2f to your computer and use it in GitHub Desktop.
BayNode Node Night 11/3/15: async/await by Adam Crabtree
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Async Await</title> | |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> | |
<style type="text/css"> | |
@import url(https://fonts.googleapis.com/css?family=Yanone+Kaffeesatz); | |
@import url(https://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic); | |
@import url(https://fonts.googleapis.com/css?family=Ubuntu+Mono:400,700,400italic); | |
body { font-family: 'Droid Serif'; } | |
h1, h2, h3 { | |
font-family: 'Yanone Kaffeesatz'; | |
font-weight: normal; | |
} | |
.remark-code, .remark-inline-code { font-family: 'Ubuntu Mono'; } | |
</style> | |
</head> | |
<body> | |
<textarea id="source"> | |
class: center, middle | |
# Async/Await | |
Simpler asynchronous JavaScript programming. | |
--- | |
# Overview | |
**3 mechanisms of asynchronous programming:** | |
1. Callbacks | |
```javascript | |
fs.readFile(__filename, (err data) => { /* ... */ }) | |
``` | |
1. Promises | |
```javascript | |
// Using pn | |
fs.readFile(__filename) | |
.then(data => { /* ... */ }) | |
.catch(err => { /* ... */ }) | |
``` | |
1. Async/Await | |
```javascript | |
// Within a function marked `async` | |
let data = fs.readFile(__filename) | |
``` | |
--- | |
# Why not callbacks? | |
--- | |
# Why not callbacks? | |
**Simplest in concept, manual and erorr-prone in practice.** | |
--- | |
# Why not callbacks? | |
**Simplest in concept, manual and erorr-prone in practice.** | |
```javascript | |
fs.stat(dirPath, (err, stats) => { | |
}) | |
. | |
``` | |
--- | |
# Why not callbacks? | |
**Simplest in concept, manual and erorr-prone in practice.** | |
```javascript | |
fs.stat(dirPath, (err, stats) => { | |
let notString = stats.isDirectory() ? "" : " not" | |
console.log(dirPath + ' is ' + notString + ' a directory.') | |
}) | |
. | |
``` | |
--- | |
# Why not callbacks? | |
**Simplest in concept, manual and erorr-prone in practice.** | |
```javascript | |
fs.stat(dirPath, (err, stats) => { | |
// Remember to handle err | |
if (err) console.log(dirPath + ' is not a directory.') | |
let notString = stats.isDirectory() ? "" : " not" | |
console.log(dirPath + ' is ' + notString + ' a directory.') | |
}) | |
. | |
``` | |
--- | |
# Why not callbacks? | |
**Simplest in concept, manual and erorr-prone in practice.** | |
```javascript | |
fs.stat(dirPath, (err, stats) => { | |
// Remember to handle err & early return | |
if (err) return console.log(dirPath + ' is not a directory.') | |
let notString = stats.isDirectory() ? "" : " not" | |
console.log(dirPath + ' is ' + notString + ' a directory.') | |
}) | |
. | |
``` | |
--- | |
# Why not callbacks? | |
**Simplest in concept, manual and erorr-prone in practice.** | |
```javascript | |
function isDir(dirPath, callback) { | |
fs.stat(dirPath, (err, stats) => { | |
// Remember to handle err & early return | |
if (err) return callback(null, false) | |
let notString = stats.isDirectory() ? "" : " not" | |
console.log(dirPath + ' is ' + notString + ' a directory.') | |
}) | |
} | |
``` | |
--- | |
# Why not callbacks? | |
**Simplest in concept, manual and erorr-prone in practice.** | |
```javascript | |
function isDir(dirPath, callback) { | |
fs.stat(dirPath, (err, stats) => { | |
// Remember to handle err and early return | |
if (err) return callback(null, false) | |
// Remember to call callback | |
callback(null, stats.isDirectory()) | |
}) | |
} | |
``` | |
--- | |
# Why not callbacks? | |
**Simplest in concept, manual and erorr-prone in practice.** | |
```javascript | |
function isDir(dirPath, callback) { | |
fs.stat(dirPath, (err, stats) => { | |
// Remember to handle err and early return | |
if (err) return callback(null, false) | |
// Remember to call callback | |
callback(null, stats.isDirectory()) | |
}) | |
} | |
``` | |
**Rinse & Repeat:** | |
```javascript | |
isDir(dirPath, (err, isDir) => { | |
// Remember to handle err & early return | |
if (err) return console.log(dirPath + ' is not a directory.') | |
let notString = stats.isDirectory() ? "" : " not" | |
console.log(dirPath + ' is ' + notString + ' a directory.') | |
}) | |
``` | |
--- | |
class: center, middle | |
# *Strawman much?* | |
Use a control-flow library! | |
** *We'll get there...* ** | |
--- | |
# Why not callbacks? | |
**Errors cannot be caught!\* ** | |
```javascript | |
. | |
``` | |
\* ... | |
--- | |
# Why not callbacks? | |
**Errors cannot be caught!\* ** | |
```javascript | |
// This will crash your server | |
isDir(__dirname, (err, stats) => { | |
throw new Error('Boom.') | |
}) | |
. | |
``` | |
\* ... | |
--- | |
# Why not callbacks? | |
**Errors cannot be caught!\* ** | |
```javascript | |
// This will crash your server | |
try { | |
isDir(__dirname, (err, stats) => { | |
throw new Error('Boom.') | |
}) | |
} catch(err) { | |
} | |
``` | |
\* ... | |
--- | |
# Why not callbacks? | |
**Errors cannot be caught!\* ** | |
```javascript | |
// This will crash your server | |
try { | |
isDir(__dirname, (err, stats) => { | |
throw new Error('Boom.') | |
}) | |
} catch(err) { | |
// Nope. | |
} | |
``` | |
\* ... | |
--- | |
class: center, middle | |
# *Really? More strawmans?* | |
Just don't throw or pass the error. | |
--- | |
# Why not callbacks? | |
**Errors cannot be caught!\* ** | |
Scenario 1: No callback | |
```javascript | |
. | |
``` | |
\* ... | |
--- | |
# Why not callbacks? | |
**Errors cannot be caught!\* ** | |
Scenario 1: No callback | |
```javascript | |
// This will NOT crash your server | |
try { | |
isDir(dirPath, (err, stats) => { | |
console.log(new Error('Boom.').stack) | |
}) | |
} catch(err) { | |
// Nope. | |
} | |
. | |
``` | |
\* ... | |
--- | |
# Why not callbacks? | |
**Errors cannot be caught!\* ** | |
Scenario 2: Pass error to callback | |
```javascript | |
function run(callback) { | |
// This will NOT crash your server | |
try { | |
isDir(dirPath, (err, stats) => { | |
callback(new Error('Boom.')) | |
}) | |
} catch(err) { | |
// Nope. | |
} | |
} | |
``` | |
\* ... | |
--- | |
# Why not callbacks? | |
**Errors cannot be caught!\* ** | |
Scenario 3: Reality | |
```javascript | |
function isDir(dirPath, callback) { | |
fs.stat(dirPath, (err, stats) => { | |
if (err) return callback(null, false) | |
callback(null, stats.isDirectory()) | |
}) | |
} | |
. | |
``` | |
\* ... | |
--- | |
# Why not callbacks? | |
**Errors cannot be caught!\* ** | |
Scenario 3: Reality | |
```javascript | |
function isDir(dirPath, callback) { | |
fs.stat(dirPath, (err, stats) => { | |
if (err) return callback(null, false) | |
callback(null, stats.isDirectory()) | |
}) | |
} | |
isDir(__dirname, (err, isDir) => { | |
console.log(__dirname + ' is ' + isDir?"":" not" + ' a directory.') | |
}) | |
``` | |
\* ... | |
--- | |
# Why not callbacks? | |
**Errors cannot be caught!\* ** | |
Scenario 3: Reality | |
```javascript | |
function isDir(dirPath, callback) { | |
fs.stat(dirPath, (err, stats) => { | |
if (err) callback(null, false) | |
callback(null, stats.isDirectory()) | |
}) | |
} | |
isDir(__dirname, (err, isDir) => { | |
console.log(__dirname + ' is ' + isDir?"":" not" + ' a directory.') | |
}) | |
``` | |
\* ... | |
--- | |
class: center, middle | |
# **BOOM.** | |
Go directly to jail. | |
Do not pass Go. | |
Do not collect your bonus. | |
--- | |
# Why not callbacks? | |
**Errors cannot be caught!\* ** | |
Scenario 3: Reality | |
```javascript | |
function isDir(dirPath, callback) { | |
fs.stat(dirPath, (err, stats) => { | |
if (err) callback(null, false) | |
callback(null, stats.isDirectory()) | |
}) | |
} | |
isDir(__dirname, (err, isDir) => { | |
console.log(__dirname + ' is ' + isDir?"":" not" + ' a directory.') | |
}) | |
. | |
``` | |
\* ... | |
--- | |
# Why not callbacks? | |
**Errors cannot be caught!\* ** | |
Scenario 3: Reality | |
```javascript | |
function isDir(dirPath, callback) { | |
fs.stat(dirPath, (err, stats) => { | |
if (err) callback(null, false) | |
callback(null, stats.isDirectory()) | |
}) | |
} | |
try { | |
isDir(__dirname, (err, isDir) => { | |
console.log(__dirname + ' is ' + isDir?"":" not" + ' a directory.') | |
}) | |
} catch(e) { | |
} | |
``` | |
\* ... | |
--- | |
# Why not callbacks? | |
**Errors cannot be caught!\* ** | |
Scenario 3: Reality | |
```javascript | |
function isDir(dirPath, callback) { | |
fs.stat(dirPath, (err, stats) => { | |
if (err) callback(null, false) | |
callback(null, stats.isDirectory()) | |
}) | |
} | |
try { | |
isDir(__dirname, (err, isDir) => { | |
console.log(__dirname + ' is ' + isDir?"":" not" + ' a directory.') | |
}) | |
} catch(e) { | |
// Nope. | |
} | |
``` | |
\* ... | |
--- | |
# Why not callbacks? | |
**Errors cannot be caught!\* ** | |
Scenario 3: Reality | |
```javascript | |
function isDir(dirPath, callback) { | |
fs.stat(dirPath, (err, stats) => { | |
if (err) callback(null, false) | |
callback(null, stats.isDirectory()) | |
}) | |
} | |
isDir(__dirname, (err, isDir) => { | |
console.log(__dirname + ' is ' + isDir?"":" not" + ' a directory.') | |
}) | |
. | |
``` | |
\* ... | |
--- | |
# Why not callbacks? | |
**Errors cannot be caught!\* ** | |
Scenario 3: Reality | |
```javascript | |
function isDir(dirPath, callback) { | |
fs.stat(dirPath, (err, stats) => { | |
if (err) callback(null, false) | |
callback(null, stats.isDirectory()) | |
}) | |
} | |
isDir(__dirname, (err, isDir) => { | |
console.log(__dirname + ' is ' + isDir?"":" not" + ' a directory.') | |
}) | |
. | |
``` | |
\* Unless you monkey-patch the runtime. | |
--- | |
# Why not callbacks? | |
**Errors cannot be caught!\* ** | |
Scenario 3: Reality | |
```javascript | |
function isDir(dirPath, callback) { | |
fs.stat(dirPath, (err, stats) => { | |
if (err) callback(null, false) | |
callback(null, stats.isDirectory()) | |
}) | |
} | |
trycatch(() => { | |
isDir(__dirname, (err, isDir) => { | |
console.log(__dirname + ' is ' + isDir?"":" not" + ' a directory.') | |
}) | |
}, (err) => { | |
// Yup. | |
}) | |
``` | |
\* Unless you monkey-patch the runtime. | |
--- | |
class: center, middle | |
# **Lesson?** | |
... | |
... | |
--- | |
class: center, middle | |
# **Lesson?** | |
Use my `trycatch` library. | |
... | |
--- | |
class: center, middle | |
# **Lesson?** | |
**No seriously,** use my `trycatch` library. | |
... | |
--- | |
class: center, middle | |
# **Lesson?** | |
~~**No seriously,** use my `trycatch` library.~~ | |
**Don't use callbacks.** | |
--- | |
# Why not callbacks? | |
**The pyramid of doom** | |
```javascript | |
foo((err, a) => { | |
if (err) return callback(err) | |
bar(a, (err, b) => { | |
if (err) return callback(err) | |
baz(b, (err, c) => { | |
if (err) return callback(err) | |
// Uhh..... | |
}) | |
}) | |
}) | |
``` | |
--- | |
# Why not callbacks? | |
**Even simple control-flows require complex non-standardized libraries** | |
```javascript | |
async.waterfall([ | |
(callback) => { | |
callback(null, 'foo') | |
}, | |
(callback, foo) => { | |
callback(null, 'bar') | |
}, | |
(callback, bar) => { | |
callback(null, 'baz') | |
} | |
], (err, baz) => { | |
console.log(baz) | |
}) | |
``` | |
--- | |
# Other requirements of the callback contract | |
--- | |
# Other requirements of the callback contract | |
* Callbacks should always be passed last | |
--- | |
# Other requirements of the callback contract | |
* Callbacks should always be passed last | |
* First argument is an error | |
--- | |
# Other requirements of the callback contract | |
* Callbacks should always be passed last | |
* First argument is an error | |
* Second argument is the result | |
--- | |
# Other requirements of the callback contract | |
* Callbacks should always be passed last | |
* First argument is an error | |
* Second argument is the result | |
* Never pass both | |
--- | |
# Other requirements of the callback contract | |
* Callbacks should always be passed last | |
* First argument is an error | |
* Second argument is the result | |
* Never pass both | |
* Errors should be `instanceof` `Error` | |
--- | |
# Other requirements of the callback contract | |
* Callbacks should always be passed last | |
* First argument is an error | |
* Second argument is the result | |
* Never pass both | |
* Errors should be `instanceof` `Error` | |
* Callbacks must never execute on the same event-loop tick | |
--- | |
# Other requirements of the callback contract | |
* Callbacks should always be passed last | |
* First argument is an error | |
* Second argument is the result | |
* Never pass both | |
* Errors should be `instanceof` `Error` | |
* Callbacks must never execute on the same event-loop tick | |
* Return value should be ignored | |
--- | |
# Other requirements of the callback contract | |
* Callbacks should always be passed last | |
* First argument is an error | |
* Second argument is the result | |
* Never pass both | |
* Errors should be `instanceof` `Error` | |
* Callbacks must never execute on the same event-loop tick | |
* Return value should be ignored | |
* Must never throw and always pass errors | |
--- | |
# Other requirements of the callback contract | |
* Callbacks should always be passed last | |
* First argument is an error | |
* Second argument is the result | |
* Never pass both | |
* Errors should be `instanceof` `Error` | |
* Callbacks must never execute on the same event-loop tick | |
* Return value should be ignored | |
* Must never throw and always pass errors | |
* Must never be called more than once | |
--- | |
# ~~Other requirements of the callback contract~~ | |
* Callbacks should always be passed last | |
* First argument is an error | |
* Second argument is the result | |
* Never pass both | |
* Errors should be `instanceof` `Error` | |
* Callbacks must never execute on the same event-loop tick | |
* Return value should be ignored | |
* Must never throw and always pass errors | |
* Must never be called more than once | |
--- | |
# Other reasons to not use callbacks | |
* Callbacks should always be passed last | |
* First argument is an error | |
* Second argument is the result | |
* Never pass both | |
* Errors should be `instanceof` `Error` | |
* Callbacks must never execute on the same event-loop tick | |
* Return value should be ignored | |
* Must never throw and always pass errors | |
* Must never be called more than once | |
--- | |
class: center, middle | |
# **Still strawmanning?** | |
Control-flow libraries handle all this! | |
--- | |
class: center, middle | |
# **Pop Quiz!** | |
Name one callback-based control-flow library | |
that handles all of the callback contract requirements. | |
--- | |
class: center, middle | |
# **Answer:** | |
Only 1. `stepup`, which uses `trycatch`. | |
https://github.com/crabdude/stepup | |
--- | |
class: center, middle | |
# **Lesson?** | |
... | |
... | |
--- | |
class: center, middle | |
# **Lesson?** | |
Use my `stepup` library. | |
... | |
--- | |
class: center, middle | |
# **Lesson?** | |
Use my `stepup` library **and my `trycatch` library.** | |
... | |
--- | |
class: center, middle | |
# **Lesson?** | |
~~Use my `stepup` library **and my `trycatch` library.**~~ | |
**Don't use callbacks.** | |
--- | |
# What about `Promise`? | |
**Better...** | |
```javascript | |
function statDir(dirPath) { | |
// With pn/fs | |
return fs.stat(dirPath) | |
} | |
function statDir(dirPath) { | |
// With songbird | |
return fs.promise.stat(dirPath) | |
} | |
``` | |
--- | |
# What about `Promise`? | |
**Better...** | |
--- | |
# What about `Promise`? | |
**Better...** | |
- Included in language: ES6, node v0.11.13, io.js v1.0 | |
--- | |
# What about `Promise`? | |
**Better...** | |
- Included in language: ES6, node v0.11.13, io.js v1.0 | |
- Automatic error handling and propagation | |
--- | |
# What about `Promise`? | |
**Better...** | |
- Included in language: ES6, node v0.11.13, io.js v1.0 | |
- Automatic error handling and propagation | |
- Elegant chaining for control-flow | |
--- | |
# What about `Promise`? | |
**Better...** | |
- Included in language: ES6, node v0.11.13, io.js v1.0 | |
- Automatic error handling and propagation | |
- Elegant chaining for control-flow | |
- Expectations are guaranteed in spec: | |
--- | |
# What about `Promise`? | |
**Better...** | |
- Included in language: ES6, node v0.11.13, io.js v1.0 | |
- Automatic error handling and propagation | |
- Elegant chaining for control-flow | |
- Expectations are guaranteed in spec: | |
- Call only once | |
--- | |
# What about `Promise`? | |
**Better...** | |
- Included in language: ES6, node v0.11.13, io.js v1.0 | |
- Automatic error handling and propagation | |
- Elegant chaining for control-flow | |
- Expectations are guaranteed in spec: | |
- Call only once | |
- Pass real errors | |
--- | |
# What about `Promise`? | |
**Better...** | |
- Included in language: ES6, node v0.11.13, io.js v1.0 | |
- Automatic error handling and propagation | |
- Elegant chaining for control-flow | |
- Expectations are guaranteed in spec: | |
- Call only once | |
- Pass real errors | |
- Pass only error or result | |
--- | |
# What about `Promise`? | |
**Better...** | |
- Included in language: ES6, node v0.11.13, io.js v1.0 | |
- Automatic error handling and propagation | |
- Elegant chaining for control-flow | |
- Expectations are guaranteed in spec: | |
- Call only once | |
- Pass real errors | |
- Pass only error or result | |
- **Still very confusing for beginners** | |
--- | |
# What about `Promise`? | |
**Promise Constructor** | |
```javascript | |
// Useful for complex or atypical flows | |
function wait(time) { | |
return new Promise((resolve, reject) => { | |
// setTimeout is not an err-back | |
setTimeout(resolve, time) | |
}) | |
} | |
``` | |
--- | |
# What about `Promise`? | |
**Promise Constructor** | |
```javascript | |
// Or compatibility with callback APIs | |
function statDir(dirPath) { | |
return new Promise((resolve, reject) => { | |
fs.stat(dirPath, (err, stat) => { | |
if (err) return reject(err) | |
resolve(stat) | |
}) | |
}) | |
} | |
``` | |
--- | |
# What about `Promise`? | |
**Problem: Errors can silently fail!** | |
```javascript | |
// You won't know this threw | |
statDir(__dirname) | |
.then(stats => { | |
throw new Error('Boom.') | |
}) | |
.then(someValue => { | |
// Nope. | |
}) | |
``` | |
--- | |
# What about `Promise`? | |
**Simple control-flows are supported, but still not obvious** | |
```javascript | |
foo() | |
. | |
``` | |
--- | |
# What about `Promise`? | |
**Simple control-flows are supported, but still not obvious** | |
```javascript | |
foo() | |
.then(a => { | |
}) | |
. | |
``` | |
--- | |
# What about `Promise`? | |
**Simple control-flows are supported, but still not obvious** | |
```javascript | |
foo() | |
.then(a => { | |
return bar(a) | |
}) | |
. | |
``` | |
--- | |
# What about `Promise`? | |
**Simple control-flows are supported, but still not obvious** | |
```javascript | |
foo() | |
.then(a => { | |
return bar(a) | |
}) | |
.then(bar => { | |
}) | |
. | |
``` | |
--- | |
# What about `Promise`? | |
**Simple control-flows are supported, but still not obvious** | |
```javascript | |
foo() | |
.then(a => { | |
return bar(a) | |
}) | |
.then(bar => { | |
return baz(c) | |
}) | |
. | |
``` | |
--- | |
# What about `Promise`? | |
**Simple control-flows are supported, but still not obvious** | |
```javascript | |
foo() | |
.then(a => { | |
return bar(a) | |
}) | |
.then(bar => { | |
return baz(c) | |
}) | |
.then(result => console.log(result)) | |
. | |
``` | |
--- | |
# What about `Promise`? | |
**Simple control-flows are supported, but still not obvious** | |
```javascript | |
foo() | |
.then(a => { | |
return bar(a) | |
}) | |
.then(bar => { | |
return baz(c) | |
}) | |
.then(result => console.log(result)) | |
// ALWAYS remember to check for errors! | |
.catch(e => console.log(e.stack)) | |
``` | |
--- | |
class: center, middle | |
# **Lesson?** | |
... | |
... | |
--- | |
class: center, middle | |
# **Lesson?** | |
Use `Promise`. | |
... | |
--- | |
class: center, middle | |
# **Lesson?** | |
Use `Promise` *for now.* | |
... | |
--- | |
class: center, middle | |
# **Lesson?** | |
~~Use `Promise` *for now.*~~ | |
**No, not really. Use async/await or async generators.** | |
--- | |
# `async/await` | |
**Our hero!* ** | |
```javascript | |
. | |
``` | |
--- | |
# `async/await` | |
**Our hero!* ** | |
```javascript | |
foo().then(a => { | |
return bar(a) | |
}) | |
.then(b => { | |
return baz(b) | |
}) | |
.then(result => console.log(result)) | |
// ALWAYS remember to check for errors! | |
.catch(e => console.log(e.stack)) | |
. | |
``` | |
--- | |
# `async/await` | |
**Our hero!* ** | |
```javascript | |
let a = await foo() | |
return bar(a) | |
}) | |
.then(b => { | |
return baz(b) | |
}) | |
.then(result => console.log(result)) | |
// ALWAYS remember to check for errors! | |
.catch(e => console.log(e.stack)) | |
. | |
``` | |
--- | |
# `async/await` | |
**Our hero!* ** | |
```javascript | |
let a = await foo() | |
bar(a) | |
.then(b => { | |
return baz(b) | |
}) | |
.then(result => console.log(result)) | |
// ALWAYS remember to check for errors! | |
.catch(e => console.log(e.stack)) | |
. | |
``` | |
--- | |
# `async/await` | |
**Our hero!* ** | |
```javascript | |
let a = await foo() | |
let b = await bar(a) | |
baz(b) | |
.then(result => console.log(result)) | |
// ALWAYS remember to check for errors! | |
.catch(e => console.log(e.stack)) | |
. | |
``` | |
--- | |
# `async/await` | |
**Our hero!* ** | |
```javascript | |
let a = await foo() | |
let b = await bar(a) | |
let result = await baz(b) | |
.then(result => console.log(result)) | |
// ALWAYS remember to check for errors! | |
.catch(e => console.log(e.stack)) | |
. | |
``` | |
--- | |
# `async/await` | |
**Our hero!* ** | |
```javascript | |
let a = await foo() | |
let b = await bar(a) | |
let result = await baz(b) | |
console.log(result) | |
// ALWAYS remember to check for errors! | |
.catch(e => console.log(e.stack)) | |
. | |
``` | |
--- | |
# `async/await` | |
**Our hero!* ** | |
```javascript | |
async function main() { | |
let a = await foo() | |
let b = await bar(a) | |
let result = await baz(b) | |
console.log(result) | |
} | |
// ALWAYS remember to check for errors! | |
.catch(e => console.log(e.stack)) | |
. | |
``` | |
--- | |
# `async/await` | |
**Our hero!* ** | |
```javascript | |
async function main() { | |
let a = await foo() | |
let b = await bar(a) | |
let result = await baz(b) | |
console.log(result) | |
} | |
main() | |
// ALWAYS remember to check for errors! | |
.catch(e => console.log(e.stack)) | |
. | |
``` | |
--- | |
# `async/await` | |
**Our hero!* ** | |
```javascript | |
async function main() { | |
let a = await foo() | |
let b = await bar(a) | |
let result = await baz(b) | |
console.log(result) | |
} | |
main() | |
.then(result => console.log(result)) | |
// ALWAYS remember to check for errors! | |
.catch(e => console.log(e.stack)) | |
. | |
``` | |
--- | |
# `async/await` | |
**Our hero!* ** | |
```javascript | |
async function main(someArg) { | |
let a = await foo() | |
let b = await bar(a) | |
let result = await baz(b) | |
console.log(result) | |
} | |
main(someArg) | |
.then(result => console.log(result)) | |
// ALWAYS remember to check for errors! | |
.catch(e => console.log(e.stack)) | |
. | |
``` | |
--- | |
# `async/await` | |
**Our hero!* ** | |
```javascript | |
async function main(someArg) { | |
let a = await foo() | |
let b = await bar(a) | |
let result = await baz(b) | |
console.log(result) | |
} | |
let result = await main(someArg) | |
. | |
``` | |
--- | |
# `async/await` | |
**Our hero!* ** | |
```javascript | |
async function main(someArg) { | |
let a = await foo() | |
let b = await bar(a) | |
let result = await baz(b) | |
console.log(result) | |
} | |
async ()=> { | |
let result = await main(someArg) | |
}() | |
``` | |
--- | |
# `async/await` | |
**Our hero!* ** | |
```javascript | |
async function main(someArg) { | |
let a = await foo() | |
let b = await bar(a) | |
let result = await baz(b) | |
console.log(result) | |
} | |
async ()=> { | |
let result = await main(someArg) | |
console.log(result) | |
}() | |
``` | |
--- | |
# `async/await` | |
**Our hero!* ** | |
```javascript | |
async function main(someArg) { | |
let a = await foo() | |
let b = await bar(a) | |
let result = await baz(b) | |
console.log(result) | |
} | |
async ()=> { | |
try { | |
let result = await main(someArg) | |
} catch(e) { | |
console.log(e.stack) | |
} | |
console.log(result) | |
}() | |
``` | |
--- | |
# `async/await` | |
**Synchronous-style Parallel IO** | |
```javascript | |
async function main(someArg) { | |
// Wait on foo, bar and baz in parallel | |
let [a, b, c] = await Promise.all([foo(someArg), bar(), baz()]) | |
return a + b + c | |
} | |
``` | |
--- | |
# `async/await` | |
**Await on Non-Promise values** | |
```javascript | |
async function main(someArg) { | |
// Wait on foo, bar and baz in parallel | |
let [a, b, c] = await Promise.all([foo(someArg), bar(), baz()]) | |
return a + b + c | |
} | |
``` | |
*Note: It's intentional that the code above is the same!* | |
--- | |
# Why is `async/await` SUPER AWESOME?! | |
1. It forces a distinction between synchronous and asynchronous functions! | |
1. Automatically handles the async contract | |
1. It composes with existing language features | |
--- | |
# `async/await` | |
**Lifecycle Phases:** | |
- Require Phase | |
--- | |
# `async/await` | |
**Lifecycle Phases:** | |
- Require Phase | |
- Blocking `*Sync` IO acceptable | |
--- | |
# `async/await` | |
**Lifecycle Phases:** | |
- Require Phase | |
- Blocking `*Sync` IO acceptable | |
- Must be completed by end of the main tick | |
--- | |
# `async/await` | |
**Lifecycle Phases:** | |
- Require Phase | |
- Blocking `*Sync` IO acceptable | |
- Must be completed by end of the main tick | |
- E.g., `require('fs')`, etc... | |
--- | |
# `async/await` | |
**Lifecycle Phases:** | |
- Require Phase | |
- Blocking `*Sync` IO acceptable | |
- Must be completed by end of the main tick | |
- E.g., `require('fs')`, etc... | |
- Configure Phase | |
--- | |
# `async/await` | |
**Lifecycle Phases:** | |
- Require Phase | |
- Blocking `*Sync` IO acceptable | |
- Must be completed by end of the main tick | |
- E.g., `require('fs')`, etc... | |
- Configure Phase | |
- The phase between blocking IO and async IO | |
--- | |
# `async/await` | |
**Lifecycle Phases:** | |
- Require Phase | |
- Blocking `*Sync` IO acceptable | |
- Must be completed by end of the main tick | |
- E.g., `require('fs')`, etc... | |
- Configure Phase | |
- The phase between blocking IO and async IO | |
- Typically, used to construct objects | |
--- | |
# `async/await` | |
**Lifecycle Phases:** | |
- Require Phase | |
- Blocking `*Sync` IO acceptable | |
- Must be completed by end of the main tick | |
- E.g., `require('fs')`, etc... | |
- Configure Phase | |
- The phase between blocking IO and async IO | |
- Typically, used to construct objects | |
- E.g., `http.createServer(..)` | |
--- | |
# `async/await` | |
**Lifecycle Phases:** | |
- Require Phase | |
- Blocking `*Sync` IO acceptable | |
- Must be completed by end of the main tick | |
- E.g., `require('fs')`, etc... | |
- Configure Phase | |
- The phase between blocking IO and async IO | |
- Typically, used to construct objects | |
- E.g., `http.createServer(..)` | |
- Initialize Phase | |
--- | |
# `async/await` | |
**Lifecycle Phases:** | |
- Require Phase | |
- Blocking `*Sync` IO acceptable | |
- Must be completed by end of the main tick | |
- E.g., `require('fs')`, etc... | |
- Configure Phase | |
- The phase between blocking IO and async IO | |
- Typically, used to construct objects | |
- E.g., `http.createServer(..)` | |
- Initialize Phase | |
- Async IO only | |
--- | |
# `async/await` | |
**Lifecycle Phases:** | |
- Require Phase | |
- Blocking `*Sync` IO acceptable | |
- Must be completed by end of the main tick | |
- E.g., `require('fs')`, etc... | |
- Configure Phase | |
- The phase between blocking IO and async IO | |
- Typically, used to construct objects | |
- E.g., `http.createServer(..)` | |
- Initialize Phase | |
- Async IO only | |
- E.g., `await http.promise.listen(8000)` | |
--- | |
# `async/await` | |
**Lifecycle Phases:** | |
- Require Phase | |
- Blocking `*Sync` IO acceptable | |
- Must be completed by end of the main tick | |
- E.g., `require('fs')`, etc... | |
- Configure Phase | |
- The phase between blocking IO and async IO | |
- Typically, used to construct objects | |
- E.g., `http.createServer(..)` | |
- Initialize Phase | |
- Async IO only | |
- E.g., `await http.promise.listen(8000)` | |
- Running Phase | |
--- | |
# `async/await` | |
**Lifecycle Phases:** | |
- Require Phase | |
- Blocking `*Sync` IO acceptable | |
- Must be completed by end of the main tick | |
- E.g., `require('fs')`, etc... | |
- Configure Phase | |
- The phase between blocking IO and async IO | |
- Typically, used to construct objects | |
- E.g., `http.createServer(..)` | |
- Initialize Phase | |
- Async IO only | |
- E.g., `await http.promise.listen(8000)` | |
- Running Phase | |
- Ready to receive requests, respond to external events, etc... | |
--- | |
# `async/await` | |
**Lifecycle Phases:** | |
- Require Phase | |
- Blocking `*Sync` IO acceptable | |
- Must be completed by end of the main tick | |
- E.g., `require('fs')`, etc... | |
- Configure Phase | |
- The phase between blocking IO and async IO | |
- Typically, used to construct objects | |
- E.g., `http.createServer(..)` | |
- Initialize Phase | |
- Async IO only | |
- E.g., `await http.promise.listen(8000)` | |
- General Phase | |
- Ready to receive requests, respond to external events, etc... | |
- E.g., `app.get('*', routeHandler)` | |
--- | |
# `async/await` | |
**Separation of Async and Sync lifecycle phases** | |
```javascript | |
async function main(someArg) => { | |
let a = await foo(someArg) | |
let b = await bar(a) | |
let c = await baz(b) | |
return result | |
} | |
async ()=> { | |
try { | |
let result = await main(someArg) | |
} catch(e) { | |
console.log(e.stack) | |
} | |
}() | |
``` | |
--- | |
# `async/await` | |
**Separation of Async and Sync lifecycle phases** | |
```javascript | |
async function main(someArg) => { | |
// General Phase | |
let a = await foo(someArg) | |
let b = await bar(a) | |
let c = await baz(b) | |
return result | |
} | |
async ()=> { | |
try { | |
let result = await main(someArg) | |
} catch(e) { | |
console.log(e.stack) | |
} | |
}() | |
``` | |
--- | |
# `async/await` | |
**Separation of Async and Sync lifecycle phases** | |
```javascript | |
// index.js | |
// Require Phase | |
let main = require('./main') | |
async ()=> { | |
try { | |
let result = await main(someArg) | |
} catch(e) { | |
console.log(e.stack) | |
} | |
}() | |
``` | |
--- | |
# `async/await` | |
**Separation of Async and Sync lifecycle phases** | |
```javascript | |
// index.js | |
// Require Phase | |
let main = require('./main') | |
// Initialize Phase | |
main(someArg).catch(err => console.log(err.stack)) | |
``` | |
--- | |
# `async/await` | |
**Separation of Async and Sync lifecycle phases** | |
```javascript | |
// index.js | |
// Require Phase | |
let main = require('./main') | |
// Configure Phase | |
process.on('uncaughtException', function(err) { | |
console.log('uncaughtException: \n\n', err.stack) | |
// IMPORTANT! (optionally, soft exit) | |
process.exit() | |
}) | |
// Initialize Phase | |
main(someArg).catch(err => console.log(err.stack)) | |
``` | |
--- | |
# `async/await` | |
**Separation of Async and Sync lifecycle phases** | |
```javascript | |
// index.js | |
// Require Phase | |
let main = require('./main') | |
// Configure Phase | |
process.on('uncaughtException', function(err) { | |
console.log('uncaughtException: \n\n', err.stack) | |
// IMPORTANT! (optionally, soft exit) | |
process.exit() | |
}) | |
process.on('unhandledRejection', (err, rejectedPromise) => { | |
console.log('unhandledRejection: \n\n', err.stack) | |
}) | |
// Initialize Phase | |
main(someArg).catch(err => console.log(err.stack)) | |
``` | |
--- | |
# `async/await` | |
**Separation of Async and Sync lifecycle phases** | |
```javascript | |
// index.js | |
// Require Phase | |
let app = require('./app') | |
// Configure Phase | |
process.on('uncaughtException', function(err) { | |
console.log('uncaughtException: \n\n', err.stack) | |
// IMPORTANT! (optionally, soft exit) | |
process.exit() | |
}) | |
process.on('unhandledRejection', (err, rejectedPromise) => { | |
console.log('unhandledRejection: \n\n', err.stack) | |
}) | |
// Initialize Phase | |
app.initialize().catch(err => console.log(err.stack)) | |
``` | |
--- | |
# `async/await` | |
**Even safer with `trycatch`** | |
```javascript | |
// index.js | |
// Require Phase | |
let app = require('./app') | |
// Initialize Phase | |
app.initialize().catch(err => console.log(err.stack)) | |
``` | |
--- | |
# `async/await` | |
**Even safer with `trycatch`** | |
```javascript | |
// index.js | |
// Require Phase | |
let app = require('./app') | |
require('trycatch') | |
// Initialize Phase | |
app.initialize().catch(err => console.log(err.stack)) | |
``` | |
--- | |
# `async/await` | |
**Even safer with `trycatch`** | |
```javascript | |
// index.js | |
// Require Phase | |
let app = require('./app') | |
require('trycatch') | |
// Configure Phase | |
process.on('uncaughtException', handleErrorAndExit) | |
process.on('unhandledRejection', handleError) | |
// Initialize Phase | |
app.initialize().catch(err => console.log(err.stack)) | |
``` | |
--- | |
# `async/await` | |
**Even safer with `trycatch`** | |
```javascript | |
// index.js | |
// Require Phase | |
let app = require('./app') | |
require('trycatch') | |
// Configure Phase | |
process.on('uncaughtException', handleErrorAndExit) | |
process.on('unhandledRejection', handleError) | |
process.on('unhandledApplicationException', handleError) | |
// Initialize Phase | |
app.initialize().catch(err => console.log(err.stack)) | |
``` | |
--- | |
# `async/await` | |
**`trycatch` also supports long-stack-traces** | |
```javascript | |
// index.js | |
// Require Phase | |
let app = require('./app') | |
let trycatch = require('trycatch') | |
// Configure Phase | |
process.on('uncaughtException', handleErrorAndExit) | |
process.on('unhandledRejection', handleError) | |
process.on('unhandledApplicationException', handleError) | |
if (process.env.NODE_ENV === 'development') { | |
trycatch.configure({'long-stack-traces'}) | |
} | |
// Initialize Phase | |
app.initialize().catch(err => console.log(err.stack)) | |
``` | |
--- | |
# `async/await` | |
**`trycatch` also supports long-stack-traces** | |
![:scale 100%](images/trycatch-long-stack-trace.png "Long Stak Traces") | |
--- | |
# `async/await` | |
**Using `async/await` today with Async Generators (`function*()/yield`)** | |
- Supported today with `--harmony` flag in node v0.11.13, io.js v1.0 | |
- Supported today **without a flag** in node v4.0 | |
- Requires a library: `blubird.coroutine(function*(){})` | |
```javascript | |
function* main(someArg) { | |
let a = yield foo(someArg) | |
let b = yield bar(a) | |
let c = yield baz(b) | |
return result | |
} | |
bluebird.coroutine(function*() { | |
try { | |
let result = yield main() | |
} catch(e) { | |
console.log(e.stack) | |
} | |
}) | |
``` | |
--- | |
class: center, middle | |
# Questions? | |
</textarea> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/styles/default.min.css"> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/highlight.min.js"></script> | |
<script src="https://gnab.github.io/remark/downloads/remark-latest.min.js"> | |
</script> | |
<script> | |
remark.highlighter.engine = hljs | |
remark.macros.scale = function (percentage) { | |
var url = this | |
return '<img src="' + url + '" style="width: ' + percentage + '" />' | |
} | |
var slideshow = remark.create({ | |
highlightStyle: 'tomorrow-night-bright' | |
}) | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment