Created
March 26, 2020 12:04
-
-
Save barlas/b26b4a01a3aa522ab23a999052b4832b to your computer and use it in GitHub Desktop.
ASYNC Code evolution in JS/ES => Callbacks => Promise => async/await
This file contains hidden or 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
/* | |
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