Getting Started with Asynchronous JavaScript
Image by moise_theodor via Pixabay

Getting Started with Asynchronous JavaScript

When I'm trying to learn something new and am finding trouble wrapping my head around the overarching concept, my first goal is to accumulate a heap of knowledge and then boil it down to a simple statement that makes sense. The key to this is the "makes sense" part, because you can--and should, if possible-- often come up with the simple statement before you have a firm (or even soft) understanding of what it really means. In the case of JavaScript (which is asynchronous by nature), here are some simple statements:

  • Asynchronous code behaves asynchronously, meaning it performs actions out of order as opposed to performing each action in the order it appears in the code.
  • Callbacks are functions that are written and called (back) later as the parameter of another function.
  • Promises are return objects that serve as a function's promise to do something when it is called; when you call the function that returns the promise, it either resolves (fulfills) or rejects the promise based on the conditions its invoked in.

If you looked over those and thought "duh," you know how to read. If you read through the previous sentence and thought "duh" again, that's a good thing. The simple statements exercise is supposed to make you say that-- That's how you know it's working. It's a "duh" now, but it will turn into an "aha!" later. The reason I love this technique is that it allows you to lay the groundwork on which you build your understanding, and it can act as a continuously evolving mantra for your study. I mean, of course asynchronous code behaves asynchronously! But what does that actually mean in the context of JavaScript code? How is it done? Why should I care? Finding the answers to those questions will give you your aha.

* Note: Before you dive in, know that this article isn't heavy on code examples. There are two small, simple programs that the console output is displayed for, but the code itself does not make an appearance here (however, it is available on Github if you want to check it out.) This particular article is focused on the main concepts behind asynchronous JavaScript rather than what they look like in practice, although I hope to do another writing more focused on that at a later date.

Asynchronous code

With purely synchronous code, the machine running the application executes its tasks in ABC order, performing and completing each action it encounters before moving on to the next; this is called "blocking" code, since it blocks other code from running until it has finished executing. Conversely, in asynchronous (non-blocking) code, the machine can address tasks outside the flow of the main program, so the program doesn't have to pause and wait for each function to be executed and completed in the order in which it is written.

A simple, real-life example is following a recipe. Let's say these are our recipe instructions:

Step 1: Boil water.

Step 2: Add rotini to water and cook for 11 minutes.

Step 3: In a large bowl, combine lemon, oil, and spices together to make the sauce.

Step 4: Strain the rotini and add it to the bowl of sauce.

If you were to perform these actions synchronously, you would get stuck waiting at step 2 for the pasta to finish cooking before you could move on to making the sauce. In contrast, if you were to perform these actions asynchronously, you could put the pasta in the pot of water and then work on combining the sauce ingredients while it was cooking.

To be Asynchronous, or not to be asynchronous?

That is the question.

Seems like an easy one to answer-- Who doesn't love multitasking? Science says humans aren't very good at it, even though we think we are, but you know who is good at it? Computers. They can do all the things, and can do many of them at the same time. Magical as that is, it doesn't always mean that they should multitask, because some tasks need to be performed in an order reliant on the outcome of other tasks (e.g., if task B needs the output of task A to run, task B must be performed only after task A completes.) It's up to you to tell the machine what actions in your code must be addressed synchronously, because--spoiler alert--your average computer isn't smart enough to figure that out on its own (yet).

Let's look at the recipe example again. In case you forgot and don't want to scroll up, here are the steps:

Step 1: Boil water.

Step 2: Add pasta to boiling water and cook for 11 minutes.

Step 3: In a large bowl, combine lemon, oil, and spices together to make the sauce.

Step 4: Strain the pasta and add it to the sauce.

Now, we already discussed how it's a waste of time to wait the full 11 minutes for the pasta to finish cooking when you could be making the sauce in that time. Those two tasks aren't dependent on one another, so they can be performed asynchronously.

But, let's say you finish making the sauce in step 3 and decide to move onto step 4 while the pasta continues boiling. A human will (hopefully) realize that you can't complete step 4 until step 2 is complete, because you don't yet have the cooked pasta to add to the sauce, so this is the point at which you'd logically wait. A computer on the other hand, won't make such a connection on its own. If you give it asynchronous code and don't tell it to wait (i.e., behave synchronously) in specific steps, it will reach step 4 in this scenario and freak-out because it doesn't have any pasta to add to the bowl of sauce.

In the screenshot below, you'll see the console output for a code version of this example of asynchronous execution gone wrong. The outcome of step 4 (the completePastaDish function) is dependent on the completion of step 2 (the cookRotini function), so executing completePastaDish before cookRotini finishes ruins the meal. Side note: The code for this is by no means Earth-shattering, but it's here on GitHub if you want to check it out.

Screenshot of a JS console displaying undesired asynchronous behavior in the recipe example

To improve on this, you can use a combination of synchronous and asynchronous code to cook the meal efficiently and logically. Let's look at some ways to do just that.

Callbacks

First off, don't use callbacks if you have a choice. This information is here to give a little background for this subject and because you may run into them in older code bases, but they are outdated. Promises and async/await (see below) are more modern options.

A callback is a function that is passed to another function as a parameter. If the callback is passed with adjacent parentheses, it will be invoked as soon as the function it was passed to is invoked. If it's passed without parentheses, it will be invoked from somewhere within the function it was passed to.

Callbacks have a bad rep because of a phenomenon that is frequently referred to as "callback hell." You can think of it as callback "inception" if you saw that movie, or if you didn't see it, you can think of it as callbacks nested within callbacks nested within callbacks. This often occurs when programmers try to force their code to run synchronously from top to bottom visually, continuously drilling down with callbacks until it ends up a giant mess of a staircase that leads to said hell.

A promise is a promise

Promise objects represent what will eventually be returned by their function (i.e., what the function promises to return at some point).

A promise takes the resolve and reject functions as parameters, one of which will end up moving the promise from a pending state to a resolved or rejected state, respectively. When writing the conditions of the resolved state, you will return the value of the successfully executed promise by passing it as the argument to the resolve function. To handle cases in which the promise is not fulfilled successfully, you will issue the return by passing the reason for rejection as the argument to the reject function, which will then throw an error.

The magic of async/await

The async keyword and await operator make a great couple. They can be a little confusing at first though, because async is used to mark a function as asychronous, while await is used inside async functions to make parts of their code behave synchronously (i.e., to make the execution flow pause, or wait, for the outcome of a specific piece of code before continuing). Oddly enough, await can't even be used in a function that's not marked as async. Perhaps opposites do attract.

Any function declared as async will return a promise; if you don't explicitly return a promise in the code, the function will automatically wrap the return value in a promise for you. This allows you to use the benefits of promises without necessarily writing the promises yourself.

As for await, you can tack it onto any promise-returning code within an async function to make the execution flow within that function halt right there until that code is done doing its thing. In the case of our recipe example, you can put await statements in the completePastaDish function (step 4) to make sure it waits for the cooked pasta (the outcome of step 2) and finished sauce (the outcome of step 3) before it declares the dish successful or ruined. Since step 2 is dependent on step 1 (boiling water), you can also utilize await there to make it pause instead of crashing because it has nothing to cook the pasta in.

Refactoring with what we learned

With a combination of promises and async/await, we can now refactor our original pasta dish making program so that is works the way we want it to. To see this slightly better piece of code, view it here on Github.

No alt text provided for this image

要查看或添加评论,请登录

Sondra Coffin的更多文章

  • Welcome to the Wonderful World of HTML

    Welcome to the Wonderful World of HTML

    HTML is a computing language that is used to “tell” computers how a web page should be structured. It is a fundamental…

  • Use the Visual Studio Code "Prophet Debugger" extension to connect to a Salesforce Commerce Cloud sandbox

    Use the Visual Studio Code "Prophet Debugger" extension to connect to a Salesforce Commerce Cloud sandbox

    I just finished working my way through a Medium article by Manu Saini regarding connecting VSCode (Visual Studio Code)…

    1 条评论
  • Pug.js Basics

    Pug.js Basics

    Pug (formerly known as Jade) is a template engine that allows you to dynamically manipulate the HTML and CSS that are…

    6 条评论
  • 10 Minute Vue App

    10 Minute Vue App

    The Intro Vue is a front-end JavaScript framework that packs a lot of the same features of other popular frameworks…

  • Git Refresher

    Git Refresher

    * * * PLEASE NOTE * * * ?As of June 2020, GitHub now refers to the "master" branch as the "main" branch by default…

  • Quick Tip: Shortcut a query in Oracle SQL Developer or SQL Server Management Studio

    Quick Tip: Shortcut a query in Oracle SQL Developer or SQL Server Management Studio

    After going back and forth copy-and-pasting or trying to remember queries for my current project, I decided wanted to…

  • My Journey into SOAP

    My Journey into SOAP

    Introduction This article embodies my understanding of SOAP-- which is admittedly simplified in some areas-- in the…

    2 条评论
  • 3 Quick Tips for Absolute Beginner Coders

    3 Quick Tips for Absolute Beginner Coders

    I've gotten a lot of messages from people across the world asking about my journey into code, so I wanted to take a few…

    2 条评论
  • Why You Should Hire a Candidate with Teaching Experience

    Why You Should Hire a Candidate with Teaching Experience

    Having been an educator for several years, I know firsthand how valuable teachers' knowledge, skills, and work ethic…

    6 条评论
  • A Teacher's To-Do List

    A Teacher's To-Do List

    I recently wrote an article for staffing managers on Why You Should Hire a Candidate with Teaching Experience, and…

    2 条评论

社区洞察

其他会员也浏览了