Im trying to wrap my head around asynchronous javascript. So I wrote a function to test some things out. What I'm trying to do is write a scenario where I'm:
My Question
As can be seen in the execution results, listed below, the cookPopcorn()
function will finish executing before makingDrinks()
is started. I don't want to have to wait for the popcorn to finish cooking before making my drinks. Is it even possible in JS to run makingDrinks()
during the execution of cookPopcorn()
or am I misunderstanding the use case of async/await?
MovieNight.js
function wait(ms) {
let start = Date.now(),
now = start;
while (now - start < ms) {
now = Date.now();
}
}
async function cookPopcorn() {
console.log("Cooking popcorn: a");
wait(2000);
console.log("Cooking popcorn: b");
wait(2000);
console.log("Cooking popcorn: c");
wait(2000);
return "unsalted";
}
async function makingDrinks() {
console.log("Making drinks");
wait(2000);
return "Drinks finished";
}
async function saltPopcorn(popcorn) {
if (popcorn.includes("unsalted")) {
console.log("sprinkling salt over popcorn");
wait(4000);
return "salted popcorn";
}
}
function startMovie(popcorn, drinks) {
console.log(`We have ${popcorn} and ${drinks}, so let's start movie...`);
}
async function setupMovieNight() {
let popcorn = await cookPopcorn(); // starts cooking the popcorn
let drinks = makingDrinks(); // Pour the drinks while the popcorn is busy cooking
let saltedPopcorn = await saltPopcorn(popcorn); // Salt the popcorn only after it is finished cooking
startMovie(saltedPopcorn, drinks); // start the movie only when the popcorn has been salted and the drinks are ready
}
setupMovieNight();
In my result cooking a,b,c finish before starting to make drinks. I would like to make drinks in-between cooking a,b or c instead of waiting for the popcorn to finish cooking.
Result
Cooking popcorn: a
Cooking popcorn: b
Cooking popcorn: c
Making drinks
sprinkling salt over popcorn
We have salted popcorn and [object > Promise], so let's start movie...
Your "wait" function is not asynchronous.
It just runs a loop setting now = Date.now()
repeatedly as fast as it can, blocking anything else from happening, for 2 seconds (or whatever).
You could reimplement it using a Promise with setTimeout:
async function wait (ms) {
return new Promise((resolve) => {
console.log(`waiting ${ms}`);
// call resolve after the time has elapsed
setTimeout(() => {
console.log('done');
resolve();
}, ms);
})
}
async function go () {
console.log('start popcorn a');
await wait(2000);
console.log('start popcorn b');
await wait(2000);
console.log('start popcorn c');
await wait(2000);
console.log('done', Date.now());
}
go();
You could of course just have cookPopcorn
do this.
async function cookPopcorn () {
return new Promise((resolve) => {
setTimeout(() => resolve('yay popcorn!'), 2000);
})
}
function noAwait() {
// notice that "do other stuff" is logged before "yay popcorn"
// because we're not awaiting cookPopcorn
console.log('do stuff, no await.');
cookPopcorn().then(result => console.log(result));
console.log('do other stuff, no await.');
}
async function withAwait() {
// logs in order because we await the popcorn before moving on.
console.log('do stuff');
const result = await cookPopcorn();
console.log(result);
console.log('do other stuff');
}
<button onClick="noAwait()">No Await</button>
<button onClick="withAwait()">With Await</button>