Javascript traditionally relied on callback functions to handle asynchronous processes. As your Javascript application grows, you would encounter “Callback Hell”, which makes your code unreadable and application error-prone. There are now several ways to combat callback hell, and make your application development experience more enjoyable.
The presentation will quickly go over the shortcomings of callback functions. It will also provide a thorough walk-through of Promises, and Generators, and combine the two paradigms to explain the new ES2016 feature async/await.
8. pretendToThink(input)
setTimeout(cb)getAnswer(3)console.log('thinking...')
inWords(4)
function inWords (answer) {
return 'The Answer is ' + answer
}
function getAnswer (num) {
return inWords(num + 1)
}
function pretendToThink (num) {
setTimeout(function cb () {
console.log(getAnswer(num))
}, 8000)
console.log('thinking...')
}
let input = 3
pretendToThink(input)
Call Stack Web API / Node C++
main()
cb
Callback Queue
console.log(getAnswer(3))
cb()
cb
Event Loop
Concurrency
9. Phew… That was a lot…
• When we talk about Single-Threaded, it means there is only one call
stack and one event loop.
• Event Loop watches the callback queue and the call stack
• If the call stack is empty, and there is something in the callback
queue, the Event Loop puts the callback function in the call stack.
15. • A Promise is a value that guarantees a future value.
• In Javascript ES6, a Promise is an object that has a then method on it.
• When instantiating a Promise Object, it takes in a function that has
two callback parameters (commonly known as resolve, and
reject)
Promises Overview
var requestName = new Promise(function (resolve) {
puts('Enter Your Name: ')
gets(function (name) {
resolve(name)
})
})
requestName.then(function (name) {
console.log(name)
})
16. var requestName = new Promise(function (resolve) {
puts('Enter Your Name: ')
gets(function (name) {
resolve(name)
})
})
requestName.then(function (name) {
console.log(name)
})
The then method
• The then method always returns a Promise
• The then method takes in two functions as parameters
• The first function is the resolve function
19. Generator Function
• function * and yield
• A generator function returns an iterator object
• an iterator object has a next method on it.
• The next method runs/continues the generator function up to yield
• The next method returns an object that has two parameters value and done
20. function * countDown () {
puts('Fuel Check')
yield 3
puts('Engine Check')
yield 2
puts('Electrical Check')
yield 1
puts('Launching...')
}
Generator
Scope
Iterator
Scope
var iterator = countDown()
var yieldedResult = iterator.next()
puts(yieldedResult.value)
yieldedResult = iterator.next()
puts(yieldedResult.value)
yieldedResult = iterator.next()
puts(yieldedResult.value)
yieldedResult = iterator.next()
puts(yieldedResult.value)
Basic Example
// object that has a next method on it
// { value: 3, done: false }
// { value: 2, done: false }
// { value: 1, done: false }
// { value: undefined, done: true }
• function * and yield
• A generator function returns an iterator object
• an iterator object has a next method on it.
• The next method runs/continues the generator function up to yield
• The next method returns an object with two params value and done
04
21. Special Yield
function * countDown () {
puts('Fuel Check')
yield 3
puts('Engine Check')
yield 2
puts('Electrical Check')
yield 1
puts('Launching...')
}
Generator
Scope
Iterator
Scope
var iterator = countDown()
var yieldedResult = iterator.next()
puts(yieldedResult.value)
yieldedResult = iterator.next()
puts(yieldedResult.value)
yieldedResult = iterator.next()
puts(yieldedResult.value)
yieldedResult = iterator.next()
puts(yieldedResult.value)
var engineStatus = yield 3yield 3
// { value: 3, done: false }
var engineCheck = {everythingGood: true}
yieldedResult = iterator.next(engineCheck)
// {everythingGood: true}
• The yield keyword can produce a
value
• The value is passed in by the following
next method call
05
22. Generators + Promises
function requestName () {
return new Promise(function (resolve) {
callbackGets(function (name) {
resolve(name)
})
})
}
function * program () {
puts('Enter Your Name: ')
var name = yield requestName()
puts('Name: ' + name)
puts('Enter Your Email: ')
var email = yield requestEmail()
puts('Email: ' + email)
puts('Enter Your Phone: ')
var phone = yield requestPhone()
puts('Phone' + phone)
puts('Enter Your Birth Date: ')
var date = yield requestBirthDate()
puts('Birth Date' + date)
}
var running = program()
running.next()
.value
.then(function (name) {
running.next(name)
.value
.then(function (email) {
running.next(email)
.value
.then(function (phone) {
running.next(phone)
.value
.then(function (birthDate) {
running.next(birthDate)
})
})
})
})
function requestEmail () {
return new Promise(function (resolve) {
callbackGets(function (email) {
resolve(email)
})
})
}
function requestPhone () {
return new Promise(function (resolve) {
callbackGets(function (phone) {
resolve(phone)
})
})
}
function requestBirthDate () {
return new Promise(function (resolve) {
callbackGets(function (birthDate) {
resolve(birthDate)
})
})
}
// iterator object
// {done: false, value: Promise}
// Promise (has a then method)
// then takes the callback
// {done: false, value: Promise}
// Promise (has a then method)
06
23. function requestBirthDate () {
return new Promise(function (resolve) {
callbackGets(function (birthDate) {
resolve(birthDate)
})
})
}
function requestPhone () {
return new Promise(function (resolve) {
callbackGets(function (phone) {
resolve(phone)
})
})
}
function requestEmail () {
return new Promise(function (resolve) {
callbackGets(function (email) {
resolve(email)
})
})
}
function requestName () {
return new Promise(function (resolve) {
callbackGets(function (name) {
resolve(name)
})
})
}
data
data
data
data
data
Refactoring Promises
name
name
email
email
phone
phone
birthDate
birthDate
data
data
data
function gets () {
return new Promise(function (resolve, reject) {
callbackGets(function (data) {
resolve(data)
})
})
}
07
24. Refactoring Runner
var running = program()
running.next()
.value
.then(function (name) {
running.next(name)
.value
.then(function (email) {
running.next(email)
.value
.then(function (phone) {
running.next(phone)
.value
.then(function (birthDate) {
running.next(birthDate)
})
})
})
})
// iterator object
// {done: false, value: Promise}
// Promise (has a then method)
// then takes the callback
// {done: false, value: Promise}
// Promise (has a then method)
function run (generatorFunc) {
}
run(program)
07
var iterator = generatorFunc() // iterator object
var yieldedObject
// undefined-> name -> email -> phone -> birthDate
function loop (resolvedValue) {
}
loop() // start the loop
yieldedObject = iterator.next(resolvedValue)
// yieldObject == {done: false, value: Promise}
if (!yieldedObject.done) {
yieldedObject.value.then(loop)
}
25. Async/await vs Generator+Promises
async function program () {
puts('Enter your name: ')
var name = await gets()
puts('Name: ' + name)
puts('Enter your Email: ')
var email = await gets()
puts('Email: ' + email)
puts('Enter your Phone: ')
var phone = await gets()
puts('Phone: ' + phone)
puts('Enter your Birth Date: ')
var date = await gets()
puts('Date: ' + date)
}
function * program () {
puts('Enter your Name: ')
var name = yield gets()
puts('Name: ' + name)
puts('Enter your Email: ')
var email = yield gets()
puts('Email: ' + email)
puts('Enter your Phone: ')
var phone = yield gets()
puts('Phone: ' + phone)
puts('Enter your Birth Date: ')
var date = yield gets()
puts('Birth Date: ' + date)
}
07
10
26. Fetch API Example
function * program () {
puts('Enter your name: ‘)
var name = yield requestName()
puts('Fetch from Server...’)
var burgerName = yield fetchRandomBurgerName()
puts(name + ' wants a ' + burgerName)
}
runs(program)
var runs = require('./lib/runs')
var fetch = require('node-fetch')
var gets = require('./lib/gets')
var puts = require('./lib/puts')
var apiRoute = 'https://bobsburger-names.azurewebsites.net/api/random'
function requestName () {
return new Promise(gets)
}
function fetchRandomBurgerName () {
return fetch(apiRoute)
.then(function (res) {
return res.json()
})
.then(function (data) {
return data.name
})
}