1 of 116

To Hell and

Callback

A history of Asynchronous Javascript

�Mike Slater

March 2, 2021

2 of 116

Remote Talk

  • Feel free to ask questions
  • There are a couple of built-in question pauses
  • http://slatron.github.io/code-examples/
  • High-Level: Will Gloss Over some Implementation Specifics

2

3 of 116

Two Topics

3

4 of 116

Two Topics

  1. How JS Handles Asynchronous Code

4

5 of 116

Two Topics

  1. How JS Handles Asynchronous Code

  1. Evolution of Asynchronous JS

5

6 of 116

“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.”

  • Brian Barbour dev.to | first google result for “javascript is single-threaded”

7 of 116

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

8 of 116

Execution Stack

Execution Stack

const name = 'Kate Bishop'

const greet = `Good Morning, ${name}`

results.log(greet)

Results

9 of 116

Execution Stack

Execution Stack

const greet = `Good Morning, ${name}`

results.log(greet)

const name = 'Kate Bishop'

Results

10 of 116

Execution Stack

Execution Stack

const name = 'Kate Bishop'

const greet = `Good Morning, ${name}`

Results

results.log(greet)

11 of 116

Execution Stack

Execution Stack

const name = 'Kate Bishop'

const greet = `Good Morning, ${name}`

Results

results.log(greet)

12 of 116

Execution Stack

Results

Execution Stack

const greet = `Good Morning, ${name}`

“name” stored in memory

results.log(greet)

13 of 116

Execution Stack

Execution Stack

Results

“name” stored in memory

“greet” stored in memory

results.log(greet)

14 of 116

Execution Stack

Execution Stack

Results

“name” stored in memory

“greet” stored in memory

“Good Morning, Kate Bishop”

15 of 116

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

16 of 116

Blocking Example

16

17 of 116

“Asynchronous programming is a means of parallel programming in which a unit of work runs separately from the main application thread...”

  • Visual Studio Magazine | first google result for “asynchronous programming definition”

18 of 116

Non-Concurrent

Synchronous

If Javascript Is

Blocking

Single-threaded

19 of 116

How does javascript do async?

Asynchronous

Non-Blocking

Concurrent

Multi-threaded

API Calls

UI Events

Form Submissions

20 of 116

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.”

  • Lee Brandt | developer.okta.com

20

21 of 116

Execution Stack == Synchronous JS

Execution Stack

Results

22 of 116

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

23 of 116

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

24 of 116

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

25 of 116

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

26 of 116

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

27 of 116

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

28 of 116

Execution Stack + Event Queue + Web Apis = Async JS

Execution Stack

WEB Apis

results.log('Third')

Results

“First”

Event Queue

SetTimeout

anonymous() in 0 ms

29 of 116

Execution Stack + Event Queue + Web Apis = Async JS

Execution Stack

WEB Apis

results.log('Third')

Results

“First”

Event Queue

SetTimeout

anonymous() in 0 ms

30 of 116

Execution Stack + Event Queue + Web Apis = Async JS

Execution Stack

WEB Apis

results.log('Third')

Results

“First”

Event Queue

anonymous()

31 of 116

Execution Stack + Event Queue + Web Apis = Async JS

Execution Stack

WEB Apis

Results

“First”

“Third”

Event Queue

anonymous()

32 of 116

Execution Stack + Event Queue + Web Apis = Async JS

Execution Stack

WEB Apis

Results

“First”

“Third”

Event Queue

results.log('Second')

33 of 116

Execution Stack + Event Queue + Web Apis = Async JS

Execution Stack

WEB Apis

Results

“First”

“Third”

“Second”

Event Queue

34 of 116

Web APIs

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

  • DOM
  • Canvas
  • Console
  • Clipboard
  • Fetch

34

35 of 116

Summary

JavaScript engine (V8, SpiderMonkey) is single-threaded

35

36 of 116

Summary

JavaScript engine (V8, SpiderMonkey) is single-threaded

Asynchronous events passed to WEB APIs

36

37 of 116

Summary

JavaScript engine (V8, SpiderMonkey) is single-threaded

Asynchronous events passed to WEB APIs

WEB APIs respond with result on Event Queue

37

38 of 116

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

39 of 116

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

40 of 116

Questions?

41 of 116

DING-DING, ROUND 2

42 of 116

Common Asynchronous Web Example

43 of 116

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

44 of 116

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!!

45 of 116

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

46 of 116

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

47 of 116

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

48 of 116

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

49 of 116

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

50 of 116

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)

51 of 116

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)

52 of 116

Pre 2012

52

Callbacks

+

XMLHttpRequest

53 of 116

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)

  • Array.map is a higher order function
  • (i) => i + 5 is the callback function

53

54 of 116

XMLHttpRequest

requestObj = new XMLHttpRequest()

requestObj.open("GET", "/location"))

requestObj.onreadystatechange = () => {

if (requestObj.readyState === 4) {

console.log(requestObj.response)

}

}

requestObj.send(null)

54

55 of 116

Execution Stack

WEB Apis

Event Queue

requestObj

requestObj.onreadystatechange = () => {

if (requestObj.readyState === 4) {

console.log(requestObj.response)

}

}

requestObj.send()

56 of 116

Execution Stack

WEB Apis

Event Queue

requestObj.onReadyStateChange

requestObj.onreadystatechange = () => {

if (requestObj.readyState === 4) {

console.log(requestObj.response)

}

}

requestObj.send()

57 of 116

Execution Stack

WEB Apis

Event Queue

requestObj.send()

requestObj.send()

58 of 116

Execution Stack

WEB Apis

Event Queue

requestObj

XMLHttpRequest(cb)

59 of 116

Execution Stack

WEB Apis

Event Queue

requestObj

XMLHttpRequest(cb)

60 of 116

Execution Stack

WEB Apis

Event Queue

requestObj

cb(response)

61 of 116

Execution Stack

WEB Apis

Event Queue

requestObj

cb(response)

62 of 116

Execution Stack

WEB Apis

Event Queue

requestObj

if (requestObj.readyState === 4) {

console.log(requestObj.response)

}

63 of 116

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

64 of 116

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

65 of 116

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

66 of 116

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

67 of 116

Inversion of Control Issue

const goodVendor = (cb) => {/*do some stuff here*/ cb(‘done’)}

const badVendor = (cb) => {/*do some stuff here*/}

67

68 of 116

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

69 of 116

IOC Fix = Not Fun

const wrappedVendor = (cb, badVendor) => {

const timeout = setTimeout(

() => { throw new Error('cb not invoked') },

1000

);

badVendor(() => { clearTimeout(timeout); cb()})

}

69

70 of 116

Questions?

71 of 116

Callbacks = Tell Me When My Table is Ready

Chau De Script

72 of 116

Promises = Give me a Buzzer

Chau De Script

73 of 116

Promises

73

An object representing the eventual result of an asynchronous operation and its resulting value

74 of 116

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

75 of 116

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

76 of 116

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

77 of 116

Promises 2012

77

ECMAscript spec released in 2012

Began as “q” library during early ECMAscript phases

78 of 116

Promise In Action

Execution Stack

WEB Apis

Results

Event Queue

new Promise((res) => {

res('Done')

})

.then(response => {

results.log(response)

})

79 of 116

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

80 of 116

Promise In Action

Execution Stack

WEB Apis

Results

Event Queue

Promise()

new Promise((res) => {

res('Done')

})

.then(response => {

results.log(response)

})

Promise

81 of 116

Promise In Action

Execution Stack

WEB Apis

Results

Event Queue

res("Done")

new Promise((res) => {

res('Done')

})

.then(response => {

results.log(response)

})

Promise

82 of 116

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

83 of 116

Promise In Action

Execution Stack

WEB Apis

Results

“Done”

Event Queue

new Promise((res) => {

res('Done')

})

.then(response => {

results.log(response)

})

84 of 116

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")

})

})

}

85 of 116

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"))

86 of 116

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

87 of 116

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)

88 of 116

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

89 of 116

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

90 of 116

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

91 of 116

Execution Stack + Event Queue + Web Apis == Async JS

Execution Stack

WEB Apis

Results

Event Queue

92 of 116

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

93 of 116

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')

94 of 116

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')

95 of 116

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)

96 of 116

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)

97 of 116

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')

98 of 116

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

99 of 116

Job Queue Example

Execution Stack

WEB Apis

Results

“First”

Event Queue

Job Queue

results.log('Fourth')

setTimeout(cb) 0ms

Promise()

100 of 116

Job Queue Example

Execution Stack

WEB Apis

Results

“First”

“Fourth”

Event Queue

Job Queue

setTimeout(cb) 0ms

Promise()

101 of 116

Job Queue Example

Execution Stack

WEB Apis

Results

“First”

“Fourth”

Event Queue

Job Queue

setTimeout(func) 0ms

Promise()

102 of 116

Job Queue Example

Execution Stack

WEB Apis

Results

“First”

“Fourth”

Event Queue

Job Queue

setTimeout(func) 0ms

res("Third")

103 of 116

Job Queue Example

Execution Stack

WEB Apis

Results

“First”

“Fourth”

Event Queue

Job Queue

setTimeout(func) 0ms

res("Third")

104 of 116

Job Queue Example

Execution Stack

WEB Apis

Results

“First”

“Fourth”

Event Queue

Job Queue

setTimeout(func) 0ms

("third") => results.log(res)

105 of 116

Job Queue Example

Execution Stack

WEB Apis

Results

“First”

“Fourth”

“Third”

Event Queue

Job Queue

setTimeout(func) 0ms

106 of 116

Job Queue Example

Execution Stack

WEB Apis

Results

“First”

“Fourth”

“Third”

Event Queue

Job Queue

results.log("Second")

107 of 116

Job Queue Example

Execution Stack

WEB Apis

Results

“First”

“Fourth”

“Third”

“Second”

Event Queue

Job Queue

108 of 116

What If There Was No .then()

108

Obligatory Matrix Meme

109 of 116

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

110 of 116

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)

}

111 of 116

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)

}

112 of 116

async / await Error Handling

Use try/catch block

112

try {

user = await get.user(userID)

} catch (err) {

console.log(`whoopsies! ${err}`)

}

113 of 116

Beyond async/await

Generators / Yield

  • More control over pausing execution

Observables

  • Subscribe-able object that emits asynchronous events

Web Sockets

  • WEB API optimized for real-time communication
  • Similar to http but much smaller size packages

113

114 of 116

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

115 of 116

Moar Talkie-Talkie, Y Not?

  • Build React application without create-react-app
  • Compare how Vue / Angular / React / JS Renders DOM
  • CSS Matures: Leaving Bootstrap and Sass Behind
  • Living Your Fullest Life Through Github Pages
  • Everything I Know About Dev I Learned From Music
  • Wellspring Client Migration

116 of 116

Sources / Links