To Hell and
Callback
A history of Asynchronous Javascript
�Mike Slater
March 2, 2021
Remote Talk
2
Two Topics
3
Two Topics
4
Two Topics
5
“Javascript is a single threaded language. This means it has one call stack… it executes code in order and must finish executing a piece of code before moving onto the next.”
JS Executes Synchronously
const name = 'Kate Bishop'
const greet = `Good Morning, ${name}`
console.log(greet)
const quiver = []
for (let i = 0; i < 10; i++) {
quiver.push(`arrow ${i}`)
}
console.log('Shoot Arrow')
quiver.shift()
7
Execution Stack
Execution Stack
const name = 'Kate Bishop'
const greet = `Good Morning, ${name}`
results.log(greet)
Results
Execution Stack
Execution Stack
const greet = `Good Morning, ${name}`
results.log(greet)
const name = 'Kate Bishop'
Results
Execution Stack
Execution Stack
const name = 'Kate Bishop'
const greet = `Good Morning, ${name}`
Results
results.log(greet)
Execution Stack
Execution Stack
const name = 'Kate Bishop'
const greet = `Good Morning, ${name}`
Results
results.log(greet)
Execution Stack
Results
Execution Stack
const greet = `Good Morning, ${name}`
“name” stored in memory
results.log(greet)
Execution Stack
Execution Stack
Results
“name” stored in memory
“greet” stored in memory
results.log(greet)
Execution Stack
Execution Stack
Results
“name” stored in memory
“greet” stored in memory
“Good Morning, Kate Bishop”
Synchronous Code is Blocking
const name = 'Kate Bishop'
const greet = `Good Morning, ${name}`
console.log(greet)
const quiver = []
for (let i = 0; i < 20; i++) {
quiver.push(`arrow ${i}`)
}
console.log('Shoot Arrow')
quiver.shift()
15
STOP
Blocking Example
16
“Asynchronous programming is a means of parallel programming in which a unit of work runs separately from the main application thread...”
Non-Concurrent
Synchronous
If Javascript Is
Blocking
Single-threaded
How does javascript do async?
Asynchronous
Non-Blocking
Concurrent
Multi-threaded
API Calls
UI Events
Form Submissions
Event Loop
“Asynchronous calls refer to calls that are moved off of Javascript’s execution stack and do some work elsewhere. ....Once the work is done, there is a function put in the event queue. Then when JavaScript’s execution stack is empty, the event loop pulls the function from the queue and pushes it onto the execution stack.”
20
Execution Stack == Synchronous JS
Execution Stack
Results
Execution Stack + Event Queue + Web Apis == Async JS
Execution Stack
WEB Apis
results.log('First')
setTimeout(function () {
results.log('Second')
}, 0)
results.log('Third')
Results
Event Queue
Execution Stack + Event Queue + Web Apis = Async JS
Execution Stack
WEB Apis
setTimeout(function () {
results.log('Second')
}, 0)
results.log('Third')
Results
results.log('First')
Event Queue
Execution Stack + Event Queue + Web Apis = Async JS
Execution Stack
WEB Apis
results.log('Third')
Results
results.log('First')
setTimeout(function () {
results.log('Second')
}, 0)
Event Queue
Execution Stack + Event Queue + Web Apis = Async JS
Execution Stack
WEB Apis
results.log('Third')
Results
results.log('First')
setTimeout(function () {
results.log('Second')
}, 0)
Event Queue
Execution Stack + Event Queue + Web Apis = Async JS
Execution Stack
WEB Apis
results.log('Third')
Results
“First”
setTimeout(function () {
results.log('Second')
}, 0)
Event Queue
Execution Stack + Event Queue + Web Apis = Async JS
Execution Stack
WEB Apis
results.log('Third')
Results
“First”
setTimeout(function () {
results.log('Second')
}, 0)
Event Queue
Execution Stack + Event Queue + Web Apis = Async JS
Execution Stack
WEB Apis
results.log('Third')
Results
“First”
Event Queue
SetTimeout
anonymous() in 0 ms
Execution Stack + Event Queue + Web Apis = Async JS
Execution Stack
WEB Apis
results.log('Third')
Results
“First”
Event Queue
SetTimeout
anonymous() in 0 ms
Execution Stack + Event Queue + Web Apis = Async JS
Execution Stack
WEB Apis
results.log('Third')
Results
“First”
Event Queue
anonymous()
Execution Stack + Event Queue + Web Apis = Async JS
Execution Stack
WEB Apis
Results
“First”
“Third”
Event Queue
anonymous()
Execution Stack + Event Queue + Web Apis = Async JS
Execution Stack
WEB Apis
Results
“First”
“Third”
Event Queue
results.log('Second')
Execution Stack + Event Queue + Web Apis = Async JS
Execution Stack
WEB Apis
Results
“First”
“Third”
“Second”
Event Queue
Web APIs
34
Summary
JavaScript engine (V8, SpiderMonkey) is single-threaded
35
Summary
JavaScript engine (V8, SpiderMonkey) is single-threaded
Asynchronous events passed to WEB APIs
36
Summary
JavaScript engine (V8, SpiderMonkey) is single-threaded
Asynchronous events passed to WEB APIs
WEB APIs respond with result on Event Queue
37
Summary
JavaScript engine (V8, SpiderMonkey) is single-threaded
Asynchronous events passed to WEB APIs
WEB APIs respond with result on Event Queue
Main Execution Thread looks at Event Queue when empty
38
Summary
JavaScript engine (V8, SpiderMonkey) is single-threaded
Asynchronous events passed to WEB APIs
WEB APIs respond with result on Event Queue
Main Execution Thread looks at Event Queue when empty
Repeat for entire lifecycle of application
39
Questions?
DING-DING, ROUND 2
Common Asynchronous Web Example
Common Asynchronous Web Example
User Info Page Comp ->
Leanne Graham
Sincere@april.biz
********************
** Latest Post:
sunt aut facere repellat provident
** Latest Post Comment
laudantium enim quasi est quidem magnam voluptate ipsam eos tempora quo
** Latest 3 Albums:
• quidem molestiae enim
• sunt qui excepturi placeat culpa
• omnis laborum odio
Common Asynchronous Web Example
User Info Page
Leanne Graham
Sincere@april.biz
********************
** Latest Post:
sunt aut facere repellat provident
** Latest Post Comment
laudantium enim quasi est quidem magnam voluptate ipsam eos tempora quo
** Latest 3 Albums:
• quidem molestiae enim
• sunt qui excepturi placeat culpa
• omnis laborum odio
Sweet Comp,
Client. You
Rock!!
Common Asynchronous Web Example
User Info Page
Get a User
Leanne Graham
Sincere@april.biz
********************
** Latest Post:
sunt aut facere repellat provident
** Latest Post Comment
laudantium enim quasi est quidem magnam voluptate ipsam eos tempora quo
** Latest 3 Albums:
• quidem molestiae enim
• sunt qui excepturi placeat culpa
• omnis laborum odio
Common Asynchronous Web Example
User Info Page
Get a User
Get Photo Albums For User
Leanne Graham
Sincere@april.biz
********************
** Latest Post:
sunt aut facere repellat provident
** Latest Post Comment
laudantium enim quasi est quidem magnam voluptate ipsam eos tempora quo
** Latest 3 Albums:
• quidem molestiae enim
• sunt qui excepturi placeat culpa
• omnis laborum odio
Common Asynchronous Web Example
User Info Page
Get a User
Get Photo Albums For User
Get Posts For User
Leanne Graham
Sincere@april.biz
********************
** Latest Post:
sunt aut facere repellat provident
** Latest Post Comment
laudantium enim quasi est quidem magnam voluptate ipsam eos tempora quo
** Latest 3 Albums:
• quidem molestiae enim
• sunt qui excepturi placeat culpa
• omnis laborum odio
Common Asynchronous Web Example
User Info Page
Get a User
Get Photo Albums For User
Get Posts For User
Get Comments For First Post
Leanne Graham
Sincere@april.biz
********************
** Latest Post:
sunt aut facere repellat provident
** Latest Post Comment
laudantium enim quasi est quidem magnam voluptate ipsam eos tempora quo
** Latest 3 Albums:
• quidem molestiae enim
• sunt qui excepturi placeat culpa
• omnis laborum odio
Common Asynchronous Web Example
User Info Page
Get a User
Get Photo Albums For User
Get Posts For User
Get Comments For First Post
Render Initial Page
Leanne Graham
Sincere@april.biz
********************
** Latest Post:
sunt aut facere repellat provident
** Latest Post Comment
laudantium enim quasi est quidem magnam voluptate ipsam eos tempora quo
** Latest 3 Albums:
• quidem molestiae enim
• sunt qui excepturi placeat culpa
• omnis laborum odio
Pre-Render Events
User Info Page (Browser loads page)
Get a User (API call)
Get Photo Albums For User (API call)
Get Posts For User (API call)
Get Comments For First Post (API call)
Render Initial Page (JS Stack)
Dependent Events
User Info Page (Browser loads page)
Get a User (API call)
Get Photo Albums For User (API call)
Get Posts For User (API call)
Get Comments For First Post (API call)
Render Initial Page (JS Stack)
Pre 2012
52
Callbacks
+
XMLHttpRequest
Callbacks
A function that accepts and/or returns another function is called a higher-order function.
Callbacks work because of higher order functions
[1,2,3].map((i) => i + 5)
53
XMLHttpRequest
requestObj = new XMLHttpRequest()
requestObj.open("GET", "/location"))
requestObj.onreadystatechange = () => {
if (requestObj.readyState === 4) {
console.log(requestObj.response)
}
}
requestObj.send(null)
54
Execution Stack
WEB Apis
Event Queue
requestObj
requestObj.onreadystatechange = () => {
if (requestObj.readyState === 4) {
console.log(requestObj.response)
}
}
requestObj.send()
Execution Stack
WEB Apis
Event Queue
requestObj.onReadyStateChange
requestObj.onreadystatechange = () => {
if (requestObj.readyState === 4) {
console.log(requestObj.response)
}
}
requestObj.send()
Execution Stack
WEB Apis
Event Queue
requestObj.send()
requestObj.send()
Execution Stack
WEB Apis
Event Queue
requestObj
XMLHttpRequest(cb)
Execution Stack
WEB Apis
Event Queue
requestObj
XMLHttpRequest(cb)
Execution Stack
WEB Apis
Event Queue
requestObj
cb(response)
Execution Stack
WEB Apis
Event Queue
requestObj
cb(response)
Execution Stack
WEB Apis
Event Queue
requestObj
if (requestObj.readyState === 4) {
console.log(requestObj.response)
}
User Info Page
Get a User
Get Photo Albums For User
Get Posts For User
Get Comments For First Post
Render Initial Page
Let’s Use Callbacks!
Leanne Graham
Sincere@april.biz
********************
** Latest Post:
sunt aut facere repellat provident
** Latest Post Comment
laudantium enim quasi est quidem magnam voluptate ipsam eos tempora quo
** Latest 3 Albums:
• quidem molestiae enim
• sunt qui excepturi placeat culpa
• omnis laborum odio
Callback Hell
firstFn((err1, data) => {
if (err1) handleErr(err1)
secondFn(data, (err2, secondData) => {
if (err2) handleErr(err2)
thirdFn(secondData, (err3, thirdData) => {
if (err3) handleErr(err3)
handleFinalResponse(thirdData)
console.log("Done, unless there was an error")
})
})
}
64
Error Handling
requestObj.onreadystatechange = () => {
if (requestObj.readyState === 4) {
try {
user = JSON.parse(requestObj.response)
} catch {
handleUniqueErrorCondition()
}
requestObj.open("GET", getUserAlbums(userID))
requestObj.onreadystatechange = () => {
65
Handling individual errors per call can be tedious.
Multiple try / catch blocks per level of callback
Shadow Variables
myAsyncFn((err, data) => {
if (err) handle(err)
myOtherAsyncFn(data, (err, secondData) => {
fn1(data, secondData, (err) => {
if (err) handle(err)
})
fn2(data, secondData, (err) => {
if (err) handle(err)
})
})
}
66
Easy to overwrite names
Hard to tell which "err" belongs to which function
Inversion of Control Issue
const goodVendor = (cb) => {/*do some stuff here*/ cb(‘done’)}
const badVendor = (cb) => {/*do some stuff here*/}
67
Inversion of Control Issue
const goodVendor = (cb) => {/*do some stuff here*/ cb(‘done’)}
const badVendor = (cb) => {/*do some stuff here*/}
goodVendor((res) => console.log(res)) // ‘done!’
badVendor((res) => console.log(res))
68
IOC Fix = Not Fun
const wrappedVendor = (cb, badVendor) => {
const timeout = setTimeout(
() => { throw new Error('cb not invoked') },
1000
);
badVendor(() => { clearTimeout(timeout); cb()})
}
69
Questions?
Callbacks = Tell Me When My Table is Ready
Chau De Script
Promises = Give me a Buzzer
Chau De Script
Promises
73
An object representing the eventual result of an asynchronous operation and its resulting value
Promises
74
An object representing the eventual result of an asynchronous operation and its resulting value
Can be in one of three states: Pending, Fulfilled, Rejected
Promises
75
An object representing the eventual result of an asynchronous operation and its resulting value
Can be in one of three states: Pending, Fulfilled, Rejected
Implements then, catch and finally functions
Promises
76
An object representing the eventual result of an asynchronous operation and its resulting value
Can be in one of three states: Pending, Fulfilled, Rejected
Implements then, catch and finally functions
Responses are wrapped in promises for chaining
Promises 2012
77
ECMAscript spec released in 2012
Began as “q” library during early ECMAscript phases
Promise In Action
Execution Stack
WEB Apis
Results
Event Queue
new Promise((res) => {
res('Done')
})
.then(response => {
results.log(response)
})
Promise In Action
Execution Stack
WEB Apis
Results
Event Queue
new Promise((res) => {
res('Done')
})
.then(response => {
results.log(response)
})
new Promise((res) => {
res('Done')
})
.then(response => {
results.log(response)
})
Promise
Promise In Action
Execution Stack
WEB Apis
Results
Event Queue
Promise()
new Promise((res) => {
res('Done')
})
.then(response => {
results.log(response)
})
Promise
Promise In Action
Execution Stack
WEB Apis
Results
Event Queue
res("Done")
new Promise((res) => {
res('Done')
})
.then(response => {
results.log(response)
})
Promise
Promise In Action
Execution Stack
WEB Apis
Results
Event Queue
new Promise((res) => {
res('Done')
})
.then(response => {
results.log(response)
})
("Done") => results.log(res)
Promise
Promise In Action
Execution Stack
WEB Apis
Results
“Done”
Event Queue
new Promise((res) => {
res('Done')
})
.then(response => {
results.log(response)
})
From Callback Hell...
firstFn((err1, data) => {
if (err1) handleErr(err1)
secondFn(data, (err2, secondData) => {
if (err2) handleErr(err2)
thirdFn(secondData, (err3, thirdData) => {
if (err3) handleErr(err3)
handleFinalResponse(thirdData)
console.log("Done, unless there was an error")
})
})
}
To Promises
firstFn()
.then(data => secondFn(data))
.then(secondData => thirdFn(secondData))
.then(thirdData => handleFinalResponse(thirdData))
.catch(err => handleErr(err))
.finally(console.log("Done, even if there was an error"))
User Info Page
Get a User
Get Photo Albums For User
Get Posts For User
Get Comments For First Post
Render Initial Page
Let’s Use Promises!
Leanne Graham
Sincere@april.biz
********************
** Latest Post:
sunt aut facere repellat provident
** Latest Post Comment
laudantium enim quasi est quidem magnam voluptate ipsam eos tempora quo
** Latest 3 Albums:
• quidem molestiae enim
• sunt qui excepturi placeat culpa
• omnis laborum odio
Optimizing Asynchronous Behavior
User Info Page (Browser loads page)
Get a User (API call)
Get Photo Albums For User (API call)
Get Posts For User (API call)
Get Comments For First Post (API call)
Render Initial Page (JS Stack)
Promise.all
Batch control of scheduling asynchronous events
With Callbacks, would have to manually count responses after calling several in a loop
Running calls asynchronously can save time
Error Handling
.catch() for broad errors, onRejected for individual
.all() will send error only to .catch() for any Promise error
.allSettled() will send full results of all Promises
Job Queue
Additional Queue Added with Promise ECMA2016
When empty, execution stack looks to this queue before the event queue
I think of it as the “Priority” queue
90
Execution Stack + Event Queue + Web Apis == Async JS
Execution Stack
WEB Apis
Results
Event Queue
Job Queue Example
Execution Stack
WEB Apis
Results
Event Queue
results.log('First')
setTimeout(function () {
results.log('Second')
}, 0)
new Promise(function (res) {
res('Third')
}).then(results.log)
results.log('Fourth')
Job Queue
Job Queue Example
Execution Stack
WEB Apis
Results
Event Queue
setTimeout(function () {
results.log('Second')
}, 0)
new Promise(function (res) {
res('Third')
}).then(results.log)
results.log('Fourth')
Job Queue
results.log('First')
Job Queue Example
Execution Stack
WEB Apis
Results
Event Queue
Job Queue
results.log('First')
setTimeout(function () {
results.log('Second')
}, 0)
new Promise(function (res) {
res('Third')
}).then(results.log)
results.log('Fourth')
Job Queue Example
Execution Stack
WEB Apis
Results
Event Queue
results.log('Fourth')
Job Queue
results.log('First')
setTimeout(function () {
results.log('Second')
}, 0)
new Promise(function (res) {
res('Third')
}).then(results.log)
Job Queue Example
Execution Stack
WEB Apis
Results
Event Queue
Job Queue
results.log('First')
setTimeout(function () {
results.log('Second')
}, 0)
new Promise(function (res) {
res('Third')
}).then(results.log)
results.log(Fourth)
Job Queue Example
Execution Stack
WEB Apis
Results
“First”
Event Queue
Job Queue
setTimeout(function () {
results.log('Second')
}, 0)
new Promise(function (res) {
res('Third')
}).then(results.log)
results.log('Fourth')
Job Queue Example
Execution Stack
WEB Apis
Results
“First”
Event Queue
Job Queue
new Promise(function (res) {
res('Third')
}).then(results.log)
results.log('Fourth')
setTimeout(cb) 0ms
Job Queue Example
Execution Stack
WEB Apis
Results
“First”
Event Queue
Job Queue
results.log('Fourth')
setTimeout(cb) 0ms
Promise()
Job Queue Example
Execution Stack
WEB Apis
Results
“First”
“Fourth”
Event Queue
Job Queue
setTimeout(cb) 0ms
Promise()
Job Queue Example
Execution Stack
WEB Apis
Results
“First”
“Fourth”
Event Queue
Job Queue
setTimeout(func) 0ms
Promise()
Job Queue Example
Execution Stack
WEB Apis
Results
“First”
“Fourth”
Event Queue
Job Queue
setTimeout(func) 0ms
res("Third")
Job Queue Example
Execution Stack
WEB Apis
Results
“First”
“Fourth”
Event Queue
Job Queue
setTimeout(func) 0ms
res("Third")
Job Queue Example
Execution Stack
WEB Apis
Results
“First”
“Fourth”
Event Queue
Job Queue
setTimeout(func) 0ms
("third") => results.log(res)
Job Queue Example
Execution Stack
WEB Apis
Results
“First”
“Fourth”
“Third”
Event Queue
Job Queue
setTimeout(func) 0ms
Job Queue Example
Execution Stack
WEB Apis
Results
“First”
“Fourth”
“Third”
Event Queue
Job Queue
results.log("Second")
Job Queue Example
Execution Stack
WEB Apis
Results
“First”
“Fourth”
“Third”
“Second”
Event Queue
Job Queue
What If There Was No .then()
108
Obligatory Matrix Meme
2017 async / await
Executes Asynchronous Code in Order like Synchronous Code
New async keyword that tells javascript there will be a pause in execution to perform an asynchronous event
Really, just syntax sugar around Promises
Included in ECMAScript 2017 Specification
109
async / await
Declare async before any function
Any contained await statements will be treated like a Promise and response bundled together
110
Async () => {
const user = await get.user(1)
console.log(user => user.name)
}
async / await
Because these are Promises, can be combined and chained
111
async () => {
const [user, albums, post] = await Promise.all([
get.user(userID),
get.userAlbums(userID),
get.userFirstPost(userID)
])
comments = await get.postComments(post.id)
renderPage(user, albums, [post], comments)
}
async / await Error Handling
Use try/catch block
112
try {
user = await get.user(userID)
} catch (err) {
console.log(`whoopsies! ${err}`)
}
Beyond async/await
Generators / Yield
Observables
Web Sockets
113
Web Workers
Separate javascript thread you have control over
Very useful for offline processing, caching
No access to browser DOM
Last Example:
https://slatron.github.io/code-examples/?example=simple_js_worker
114
Moar Talkie-Talkie, Y Not?
Sources / Links