Lect04 Asynchronous JavaScript
Lect04 Asynchronous JavaScript
Lecture 4
Asynchronous JavaScript
Today’s Contents
(function() {
...
function init() {
console.log('page loaded');
qs('button').addEventListener('click', clickHandler);
showMenu();
}
function showMenu() {
id('menu').classList.remove('hidden');
}
function clickHandler() {
/* Your code */
}
})();
Callbacks?
Callbacks are a very powerful feature in event-driven programming.
It's useful to have the ability in the JavaScript language to pass callback functions as
arguments to other functions like addEventListener and setTimeout in JS
Asynchronous Programming
The JS programs we've been writing are naturally asynchronous
We pass functions as arguments to other functions so that we can 'call back later' once
we know something we expect occurred.
We’ve already been writing asynchronously!
btn.addEventListener('click', callbackFn);
btn.addEventListener('click', function() {
...
});
btn.addEventListener('click', () => {
...
});
setTimeout(callbackFn, 2000);
setTimeout(function() {
...
}, 2000);
setTimeout(() => {
...
}, 2000);
Why is JavaScript so different?
Java, and other compiled languages, are often used to build systems.
● Objects are great to compose together to build complex systems.
● Systems must be reliable - a benefit of strict types, compiling, and well-defined
behavior in Java.
JavaScript is used to interact and communicate.
● It listens.
● It responds.
● It requests.
While programs in Java often have a well-defined specification (behavior),
programs in JS has to deal with uncertainty (weird users, unavailable servers, no
internet connection, etc.)
What if?
let myBtn = qs('button:nth-child(1)');
while (!myBtn.clicked) {
// cross our fingers
}
console.log('Finally Been Clicked T_T');
requestMenu();
// twiddle thumbs
orderPizza();
// twiddle thumbs
verifyPizza();
// twiddle thumbs
eatPizza();
// twiddle thumbs
payForPizza();
Callback (again) to Callbacks:
function order() {
setTimeout(function() {
makeRequest('Requesting menu...');
We can imagine all setTimeout(function() {
of these steps as a makeRequest('Ordering pizza...');
setTimeout(function() {
series of callbacks, makeRequest('Checking pizza...');
depending on the setTimeout(function() {
makeRequest('Eating pizza...');
event previous to setTimeout(function() {
makeRequest('Paying for pizza...');
them: setTimeout(function() {
let response = makeRequest('Done! Heading home.');
console.log(response);
}, ?);
}, ?);
}, ?);
}, ?);
}, ?);
}, ?);
}
Callback (again) to Callbacks:
function order() {
setTimeout(function() {
makeRequest('Requesting menu...');
setTimeout(function() {
makeRequest('Ordering pizza...');
setTimeout(function() {
makeRequest('Checking pizza...');
setTimeout(function() {
makeRequest('Eating pizza...');
setTimeout(function() {
makeRequest('Paying for pizza...');
setTimeout(function() {
let response = makeRequest('Done! Heading home.');
console.log(response);
}, ?);
But how long should }, ?);
}, ?);
we wait before }, ?);
running each step? }, ?);
}, ?);
}
Wouldn’t it be Nice...
… if we could do this?
orderPizza()
.then(verify)
.then(eat)
.then(pay)
.catch(badPizza);
Promises
Promises on MDN
Creating a Promise
Function Description
let promiseObj = new Creates a new Promise object with the executorFn
Promise(executorFn)
You define this function and pass it into the Promise constructor
Back to that Pizza
The functions passed to then can pass their returned values to the
next then callback
function eat(value) {
return value + ', and now it\'s gone';
}
You can also return other promises, which halt the execution of the
next then callback until it's resolved
function eat() {
return new Promise(eatExecutor);
}
(*) Even if the Promise resolves immediately, any .then() chained onto it will be put into
the microtask queue, whose tasks will only start running once all other synchronous code
has finished.
Rejecting a Pizza
- A Promise is used when we don't want to halt the main flow of execution
but are dealing with a task that takes an uncertain amount of time
- Example: requesting information from a server
- What if the server is down? What if there is an error in what I get
back? What if I made a request with inaccurate information?
Uncertainty
● File I/O
● Database transactions
● HTTP requests
○ Resource doesn't exist (404)
○ Really long response time or server is down
○ Bad internet connection
function executor(resolve) {
resolve('Woohoo!');
}
function executor(resolve) {
resolve('Woohoo!');
}
function processStr(val) {
// mellow out that message a bit
return val.toLowerCase().replace('!', '');
}
function executor(resolve) {
resolve('Woohoo!');
}
function processStr(val) {
// mellow out that message a bit
return new Promise(function(resolve) {
resolve(val.toLowerCase().replace('!', ''));
});
}
function processStr(val) {
// mellow out that message a bit
return new Promise(function(resolve) {
setTimeout(function() {
resolve(val.toLowerCase().replace('!', ''));
}, 5000);
});
}
Now, thenPromise is "PENDING" and won't resolve until the promise returned by
processStr resolves.
Promises to the Rescue
orderPizza()
.then(eat)
.then(pay)
.catch(badPizza);
Async/Await ● Using async/await
● Catching errors
● Async functions
What if?
function nextBtnClick() {
return new Promise(function (resolve) {
let myBtn = qs('button:nth-child(2)');
myBtn.addEventListener('click', resolve);
});
}
firstBtnClick()
.then(() => { console.log('Finally Been Clicked'); })
.then(nextBtnClick)
.then(() => { console.log('Click 2'); });
What if? (with Promises + A Little Syntactic Sugar)
function firstBtnClick() {
return new Promise(function (resolve) {
let myBtn = qs('button:nth-child(1)');
myBtn.addEventListener('click', resolve);
});
}
function nextBtnClick() {
return new Promise(function (resolve) {
let myBtn = qs('button:nth-child(2)');
myBtn.addEventListener('click', resolve);
});
}
await firstBtnClick();
console.log('Finally Been Clicked');
await nextBtnClick();
console.log('Click 2');
Mind = Blown
await firstBtnClick();
console.log('Finally Been Clicked');
await nextBtnClick();
console.log('Click 2');
Async/Await
"Syntactic sugar" that wraps a function's return in a promise
Allows code to "wait" for the thing to return.
is the same as
function sayHelloAsync(name) {
return new Promise(function(resolve) {
resolve("Hello " + name);
});
}
Back to that Pizza
You can also return other promises, which halt the execution of the next then
callback until it's resolved
try {
let pizza = await new Promise(orderExecutor);
console.log("Woohoo, let's eat!");
} catch (error) {
console.log(error);
}
Error handling with async/await
The catch statement will catch any errors that occur in the then block (whether
it’s in a Promise or a syntax error in the function), similar to the .catch in a
promise chain
When Do I Need the Keyword async?
For any function that is await'd, but that doesn't return a promise (although it'll still work
to add async even if it does)
let bestCourse = {
dept : "FIT",
code : 62,
qtr : "fall2023",
sections : ["AB", "AC", "AD", "AE"]
};
let data = {
"name": "FIT",
"course-num": 101,
"hello" : function() {
console.log("welcome to FIT101!");
},
"age" : "i am not going to tell y'all how old i am",
"favorites": ["survivor", "podcasts", "peanut butter"]
};
console.log(data.favorites[1]); // podcasts
data.hello(); // welcome to FIT101!
console.log(data["course-num"]); // 101
console.log(data.name); // FIT
Examples of JS objects we’ve seen so far
● DOM elements
● document, window
JavaScript Objects vs. JSON
Since JSON is ideal for communicating across different types of systems, you can't
put Javascript functions in JSON. Other languages wouldn't be able to read JSON
effectively if it had Javascript code in it.
(This is also why Dates and RegExps can't go into the JSON object -- other languages
wouldn't know how to interpret them for what they are.)
There are a few other JSON rules which you can get more details in the reading.
Numerous validators/formatters available, e.g. JSONLint