DEV Community

Cover image for Understanding JavaScript async/await in 7 seconds

Understanding JavaScript async/await in 7 seconds

Wassim Chegham on June 12, 2019

Have you been struggling to learn the new Async/Await syntax in ES2015? Well, Good news, here is a short 7 seconds animations to help you visually ...
Collapse
 
bradtaniguchi profile image
Brad

Callback hell -> promise chaining -> async await (in an IIFE)

Next step is understanding Observables in 10 seconds (which isn't possible unfortunately 😒 )

But all kidding aside nice callback to an older twitter post ;D

Collapse
 
wolfhoundjesse profile image
Jesse M. Holmes
getData(a).pipe(
  concatMap(b => getMoreData(b)),
  concatMap(c => getMoreData(c)),
  concatMap(d => getMoreData(d)),
  concatMap(e => getMoreData(e))
).subscribe({ next: e => console.log(e)})
Collapse
 
wassimchegham profile image
Wassim Chegham

Thank you. A tweet worths thousand words, right? _^

Collapse
 
sandeepbalachandran profile image
Sandeep Balachandran

I didnt get it in 7 seconds. Not even after 3 minutes

Collapse
 
crongm profile image
Carlos Garcia β˜…

I too struggle with async programming. It's not just about the code, but the mindframe. I recommend reading the Async & Performance book in the You Don't Know JS series. It's helped me a lot to understand the concepts and logic behind async.

Collapse
 
wassimchegham profile image
Wassim Chegham

How can I help you understand this?

Collapse
 
sandeepbalachandran profile image
Sandeep Balachandran

I will get back to this in the weekend will definetly ping you for more explanations.

Collapse
 
madslundt profile image
Mads • Edited

How do you implement the following with async/await:

getProfile(this.id).then(data => { this.profile = data; });

getComments(this.id).then(data => { this.comments = data; });

getFriends(this.id).then(data => { this.friends = data; });

Be aware that I can't use Promise.all because that would wait for all promises and require that all promises are resolved successfully.
What I want is to set the variables when a promise finishes and not wait for the others.

Collapse
 
nirmalpatel59 profile image
nirmalpatel59
(async () => {
   this.profile = await getProfile(this.id);
   this.comments = await getProfile(this.id);
   this.friends = await getProfile(this.id);
})();
Collapse
 
frenkix profile image
Goran Jakovljevic • Edited

It should be in async function, then call it just like:

this.profile = await getProfile(this.id);
this.comments = await getComments(this.id);
this.friends = await getFriends(this.id);

Collapse
 
madslundt profile image
Mads • Edited

The problem with this approach is that getProfile is blocking getComments and getFriends. Once getProfile is resolved successfully, getComments runs and blocking getFriends. When getComments is resolved successfully, getFriends finally runs.

That is not the same thing as running them parallel. That is more like the following:

getProfile(this.id).then(profileData => {
  this.profile = profileData;
  getComments(this.id).then(commentsData => {
    this.comments = commentsData;
    getFriends(this.id).then(friendsData => {
      this.friends = friendsData;
    });
  });
});
Thread Thread
 
frenkix profile image
Goran Jakovljevic • Edited

Ah sorry, I didn't read it correctly.

Yeah, promise.all wont resolve with a single reject, so what I would do is return an error as a normal result instead of rejecting, and then filter out errors after promises finish. Until they make something like .every available for .all as well.

And while it might be strange to have errors passing into resolve, as long as your code is expecting them, I see nothing wrong in it.

Thread Thread
 
wassimchegham profile image
Wassim Chegham • Edited

Hey, so for your particular use case, I would try this:

const profilePromise = getProfile(this.id);
const commentsPromise = getComments(this.id)
const friendsPromise = getFriends(this.id);

this.profile = await profilePromise;
this.comments = await commentsPromise;
this.friends = await friendsPromise;

Here is a working proof of concept:

const func = (t, f) => new Promise( (res, rej) => { setTimeout( () => res(f()), t )  } )
const a = func(2000, () => console.log('func 1'));
const b = func(0, () => console.log('func 2'));
const c = func(0, () => console.log('func 3'));
const [f1, f2, f3] = [await a, await b, await c];

console.log(f1, f2, f3);

Thread Thread
 
madslundt profile image
Mads

To run them parallel there is this approach:

const result = await Promise.all([
  getProfile(this.id), 
  getComments(this.id),
  getFriends(this.id),
]);

However, this waits for all to finish and does not replace the first I descried, as I see it?

As I see it the only way to avoid this with async/await is to wrap the promises into their own function and run it in Promise.all. Like this (haven't tested this yet):

await Promise.all([
  async () => { this.profile = await getProfile(id); },
  async () => { this.comments = await getComments(id); },
  async () => { this.friends = await getFriends(id); },
]);
Thread Thread
 
prasannasridharan profile image
Prasanna-Sridharan

Hi,
As I see it, in both cases the inner methods run async and the end promise is awaited so it would not be blocking the current thread anyway.
When I get time, will execute 2nd e.g. but highly doubt if it has any difference in behavior compared to 1st.

Collapse
 
fervero profile image
Maciej BΓ³jko • Edited

To this day, I fail to see the wisdom of writing b => getMoreData(b), when mere getMoreData means exactly the same thing. Because really

getData()
  .then(a => getMoreData(a)) // repeat four times

is an equivalent of

const getMoreDataButWrappedWithOneMoreFunction = x => getMoreData(x);

getData()
   .then(getMoreDataButWrappedWithOneMoreFunction) // repeat four times

What is the point of getMoreDataButWrappedWithOneMoreFunction, I wonder? Why not just put getMoreData there?

It seems as if the moment a developer learn that in JS he can define a function in the middle of some expression, he immediately forgets he isn't obliged to.

Collapse
 
emptyother profile image
emptyother

We still need to wrap the method in an arrow function to keep this in the correct context, unfortunately.

class myClass {
    myPrivateVal = "appendedValue";
    appendMyPrivateVal(argument) {
        return argument + ' ' + this.myPrivateVal;
    }
}
async function someAsyncMethod() {
    return new Promise<string>((resolve) => setTimeout(() => resolve("asyncResult"), 1000));
}
var myInstance = new myClass();
(async () => {
    const result = someAsyncMethod().then(myInstance.appendMyPrivateVal);
    result; // asyncResult undefinedβ€ˆ
    const result2 = someAsyncMethod().then((arg) => myInstance.appendMyPrivateVal(arg));
    result2; // asyncResult appendedValue
})();

I really hate this sometimes.

Collapse
 
wassimchegham profile image
Wassim Chegham

Hey Maciej, thanks for reaching out.

So to give you some context, the code sample in the animation is meant to be easy for beginners to understand and it is not supposed to be too clever. So this was done on purpose in order to show the flow of all the parameters.

However, if you still prefer passing function references instead of function invocations, I have already made another animation illustrating this:

Cheers.

Collapse
 
webdeasy profile image
webdeasy.de

That's just awesome! :)

Collapse
 
jamonjamon profile image
Jaimie Carter

Woah! That's awesome. Thank you.

Collapse
 
wassimchegham profile image
Wassim Chegham

Glad you like it πŸ˜‰

Collapse
 
jamonjamon profile image
Jaimie Carter

Callbacks and Async are a struggle. Each item in itself is easy to grasp, however when it's a whole lot of callbacks....

Collapse
 
9naimongkolpoj profile image
Nai Mongkolpoj

awesome! :)

Collapse
 
carlillo profile image
Carlos Caballero

Simple, Direct and awesome.

Thanks @manekinekko

Collapse
 
davidcanhelp profile image
David Christian Liedle

Very nice! It moves a bit fast though. Just making a slow-motion version would help me grok it in real time, and I already had a pretty good grip on async/await from my time with Dart.