May 18, 2018

Cooking with JS asynchronously

What the hell are callbacks, why do I need to promise anything, what's all the fuss about async/await stuff?

If you have ever asked those questions yourself. Here is a quick recipe how to not get confused.

First, let's cook some Carbonara๐Ÿ”ฅ.

To do this, we basically need few things (the example is simplified)

๐Ÿ”ธ Cook pasta

๐Ÿ”ธ Cook bacon

๐Ÿ”ธ Mix the two

๐Ÿ”ธ Throw a bit of cheese in

Now that the functions are done, let's call them.

Couldn't be easier, right? What we end up to see in the console is a nice list of our actions.

Ready to get your lunch? Not so fast. To the left from the log you can see the time at which the function was fired. Looks like we outperformed Michelin star chefs and got pasta and bacon ready in just a few milliseconds.

Doesn't look too realistic..

Let's bring us back to reality and simulate at least a tiny lag in the first 2 functions. Don't worry too much about the setTimeout function for now, what it does is simply waits for a bit and then logs the stuff we want.

Shouldn't make it catastrophic, let's see how the console looks right now.

Shit. Something clearly got out of control. One cannot add bacon to pasta, put cheese on top before those 2 are not ready.

This is what real life looks like though. And the problem we experienced here has something to do with the fact that all functions were fired simultaneously. In other words, JS didn't care whether we finished cooking pasta, it went on launching other functions in the meantime.

Since the example was simplified, you can only imagine what kind of damage to your app would this bring in the real world.

Callbacks

One way to deal with that is to use something known as callbacks. Regardless your favourite computer language, this will refer to a "call after" function, or in other words, anything executable that can be passed as an argument in your code.

Sounds, realistic. Let's give it a shot. To show how the callbacks work, the most simplified example will look like below:

Also we are creating another function to notify us when the cooking has been finished.

Then calling all function and the first 2 with callbacks.

Looks like it worked. But bear in mind that the purpose here is to just show you the concept of callbacks. And you can guess that the more functions you have the more complicated it will get. Eventually with this technique, you might end up having loads of functions calling each other here and there. This has a huge potential to make your code massive and unreadable.

That's where the promises come in.

Promises

To put it simply, promises help us to wrap our functions and wait till something returns before moving on to the next function.

In our case, we would like to cook the pasta first, then do the bacon and then all the mixing/finishing stuff.

This is how the implementation can look like. We creating a promise, which will simply return the check emoji on success (on so-called 'resolve') and this will happen after a short time lag.

We then take this result and push it to our prepare() function. The prepare function has also been slightly modified and now also returns the dish after writing it to the console. This should then push it to the next prepare() and so on.

Looks good, we are now waiting for pasta to be prepared, then do the bacon when the pasta is ready, then the rest. This called chaining as we chain through all the .then.

The above helps us to ensure we wait till the promise is resolved and then start cooking pasta. One example of how functions can be fired asynchronously.

In the real world, you normally return something from the function then send it to the next function to use. For example, you could fetch some data from an API, parse it and then send to the next function (which would fail to properly run without getting something from the API first).

The above example again, was simplified to familiarise you with the concept, in real life you will also specify what happens if the promise was rejected and catch errors in each .then

While this is still a better, cleaner solution than callbacks - it can also get pretty messy.

Sync/Await

Another way, which is still fairly new and was introduced with ES8 is sync and await. And the main goal is.. well make your code cleaner and smaller again.

The idea here is extremely simple. We have to create a new async function, which is a fancy way of making the functions inside work asynchronously and specify whatever part of it needs to calm down and wait for a bit. This can nicely work with promises, but you can also just wait till the function returns you something.

So for now let's actually drop in a delay for each of the actions to simulate real case. For this we can create a simple function:

Similar to the Promise example, we are creating a delay and on success it should pass the element further in the chain. Also, as it would happen in normal world, we substituted all our console.log() with return , so that now all functions should return something.

Finally, instead of calling all functions one by one, we are going to create a separate function for this and call it later. And all you have to remember is this 2 things:

๐Ÿ”ธ You need to use the word async next to the function - this will allow usage of await and will make your code to work asynchronously

๐Ÿ”ธ You will have to specify await where something needs to wait till it gets the something back

And please - Do not try to use awaits without specifying async first and do not attempt to await for something in the top level - this wouldn't work. The 2 aforementioned points are must-have.

By looking at the code above, we should expect to see the following process:

'start cooking' will be printed in console โ†’ then the variable will wait for 1250ms simulating the time delay for the specified functions. After each function we are also printing out whatever it got after waiting.

Remember, we are now returning things, not printing them in the console. So our combine variable for example will have to use dish and dish2 to mix them and won't be able to do it until both are done.

Finally, we are also adding a nice checkbox โœ… after the element showing us that it has been retrieved from the waitTillFinish() function.

Here, let's have a look what we got.

As you can see now we doing it all with approx. a 1 second delay (still to quick, but good for demonstration). For combination we obviously getting 2 checks meaning pasta and bacon are ready and then the mixing is also done.

You can try it yourself in the Chrome Dev tools, some things will differ, like you won't have to use .this word and probably will need to specify function Name when creating one.

Now that is done go make yourself some Carbonara and make sure you don't add cheese until pasta and bacon are done (wouldn't be good).

Join our telegram @thefrontend