Skip to content

Instantly share code, notes, and snippets.

@barlas
Created March 26, 2020 12:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save barlas/b26b4a01a3aa522ab23a999052b4832b to your computer and use it in GitHub Desktop.
Save barlas/b26b4a01a3aa522ab23a999052b4832b to your computer and use it in GitHub Desktop.
ASYNC Code evolution in JS/ES => Callbacks => Promise => async/await
/*
Presentation Parts
Take away: aysnc/await uses promises and promises build with callbacks.
ASYNC Code evolution in JS/ES =>
Callbacks => Promise => async/await
SECTION 1
How aysnc/await works?
- Async/await is actually just syntax sugar built on top of promises.
SECTION 2
Which order aysnc await code executes?
V8 Interpreter's code execution order explained with Event Queue.
SECTION 3
How does promises works?
Previous solution for asynchronous code are callbacks.
References:
- https://www.promisejs.org/implementing/
- https://stackoverflow.com/questions/23772801/basic-javascript-promise-implementation-attempt/23785244#23785244
- https://javascript.info/async-await
- https://alligator.io/js/async-functions/
*/
// SECTION 1 - How aysnc await works?
let log1 = ()=>{}
//log1 = console.log
// ASYNC AWAIT uses same implementation methods with PROMISES
async function funA() {
return `funA`
}
// resolves with then() method
funA().then(data => log1(data)) // logs: 123
async function funB() {
return Promise.resolve(`funB`)
}
funB().then(data => log1(data)) // logs: 123
async function funC() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve(`funC`), 1000)
})
let result = await promise // wait until the promise resolves (*)
// Can’t use await in regular functions, only in async functions!
log1(result) // "done!"
}
funC()
// SECTION 2
let log2 = ()=>{}
log2 = console.log
// V8 Interpreter's code execution order explained
// Created a function with returns a Promise
function scaryClown() {
return new Promise(resolve => {
setTimeout(() => {
resolve(`🤡`)
}, 2000)
})
}
async function printMsg() {
log2(`1st <-- EXECUTION ORDER`)
const msg = await scaryClown() // These lines below won't execute till Promise is resolved
log2(`2nd <-- EXECUTION ORDER: ${msg}`) // returns 🤡
return msg
}
log2(`3rd <-- EXECUTION ORDER`)
// Handling `printMsg()` function as a Promise
printMsg().then(data => log2(`4th <-- EXECUTION ORDER: ${data}`)) // returns 🤡
log2(`5th <-- EXECUTION ORDER`)
// Invoking `msg()` function as is will return [object Promise]
const log = console.log
//debugger
// Promise has 3 states
var PENDING = 0
var FULFILLED = 1
var REJECTED = 2
// Custom Promise statement
function PromiseNaked(fn) {
if (typeof this !== 'object')
throw new TypeError('Promises must be constructed via new')
if (typeof fn !== 'function')
throw new TypeError('fn must be a function')
// Store state which can be PENDING, FULFILLED or REJECTED
var state = PENDING
// Store value once FULFILLED or REJECTED
var value = null
// Store sucess & failure handlers
var handlers = []
function resolve(result) {
try {
var then = getThen(result)
if (then) {
doResolve(then.bind(result), resolve, reject)
return
}
state = FULFILLED
value = result
handlers.forEach(handle)
handlers = null
} catch (e) {
reject(e)
}
}
function reject(error) {
state = REJECTED
value = error
handlers.forEach(handle)
handlers = null
}
function handle(handler) {
if (state === PENDING) {
log(handlers)
handlers.push(handler)
} else {
if (state === FULFILLED && typeof handler.onFulfilled === 'function') {
handler.onFulfilled(value)
}
if (state === REJECTED && typeof handler.onRejected === 'function') {
handler.onRejected(value)
}
}
}
this.done = function (onFulfilled, onRejected) {
// ensure we are always asynchronous
setTimeout(function () {
handle({
onFulfilled: onFulfilled,
onRejected: onRejected
})
}, 0)
}
this.then = function (onFulfilled, onRejected) {
var self = this
return new PromiseNaked(function (resolve, reject) {
return self.done(function (result) {
if (typeof onFulfilled === 'function') {
try {
return resolve(onFulfilled(result))
} catch (ex) {
return reject(ex)
}
} else {
return resolve(result)
}
}, function (error) {
if (typeof onRejected === 'function') {
try {
return resolve(onRejected(error))
} catch (ex) {
return reject(ex)
}
} else {
return reject(error)
}
})
})
}
doResolve(fn, resolve, reject)
}
/**
* Check if a value is a Promise and, if it is,
* return the `then` method of that promise.
*
* @param {Promise|Any} value
* @return {Function|Null}
*/
function getThen(value) {
var t = typeof value
if (value && (t === 'object' || t === 'function')) {
var then = value.then
if (typeof then === 'function') {
return then
}
}
return null
}
/**
* Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
*
* Makes no guarantees about asynchrony.
*
* @param {Function} fn A resolver function that may not be trusted
* @param {Function} onFulfilled
* @param {Function} onRejected
*/
function doResolve(fn, onFulfilled, onRejected) {
var done = false
try {
fn(function (value) {
if (done) return
done = true
onFulfilled(value)
}, function (reason) {
if (done) return
done = true
onRejected(reason)
})
} catch (ex) {
if (done) return
done = true
onRejected(ex)
}
}
//const myPromise = new Promise(function(resolve, reject) {
const myPromise = new PromiseNaked(function(resolve, reject) {
if (true) {
resolve(`FIRST`)
} else {
reject(`ERROR`)
}
}).then(function(data) {
log(`1. Result: ${data}`)
return `SECOND`
}).then(function(data) {
log(`2. Result: ${data}`)
return `THIRD`
})
myPromise.then(function(data) {
log(`3. Result: ${data}`)
})
log(myPromise) // logs: Promise { <pending> }
setTimeout(function() {
log(myPromise) // logs: Promise { 'THIRD' }
},1000)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment