Discover: JavaScript Async/Await
Working with asynchronous programming using JavaScript.

Discover: JavaScript Async/Await Working with asynchronous programming using JavaScript.

JavaScript Promises and async/await are both used for handling asynchronous operations in JavaScript. Asynchronous programming enables non-blocking execution which is useful for tasks that involve waiting for external resources like network requests, file I/O, database queries, or user input. Instead of waiting for these operations to be completed, the program can continue executing other tasks and handle the results of the asynchronous operations once they are available. It allows for more efficient utilization of system resources and enables the creation of highly interactive and performant software systems.

What is JavaScript Async/Await?

JavaScript’s async/await is like a magic trick that turns tricky asynchronous code into something as easy to understand as a step-by-step recipe. When you use async before a function, it gains the ability to handle asynchronous tasks. Placing await in front of a task inside that function makes JavaScript pause and wait for the task to finish, just like waiting for a cake to bake before moving on to the next step. This way, even though JavaScript works in one sequence, it can manage multiple tasks efficiently.

async Function: Bridging the Asynchronous Gap

When you declare a function as async, you're essentially saying that this function will deal with asynchronous operations. It returns a Promise implicitly, allowing you to work with it using Promise-based methods like .then() and .catch().

await Keyword: Time for Consideration

The await keyword is used inside an async function to indicate that the code execution should pause at this point. It waits for the Promise to be settled, meaning that it waits for the asynchronous operation to complete and its result to be available. This ensures that you're not proceeding with the function until you have the awaited result.

Asynchronicity within a Single Thread

JavaScript, at its core, is single-threaded, which means it processes one task at a time. However, asynchronous operations can be effectively managed without locking the entire program. When an async function encounters an await, it doesn't block the whole execution. Instead, it momentarily steps aside, allowing other tasks to proceed. This behavior is possible because of the event loop and callback queues, which handle the non-blocking execution of asynchronous operations.

In essence, async/await in JavaScript allows you to write asynchronous code that's not only more human-readable but also effectively manages the single-threaded nature of the language. It ensures that asynchronous tasks are handled without locking the program, making the codebase more efficient, maintainable, and responsive.

When do you want to consider implementing Async/Await?

Async/await is an approach in JavaScript that can be compared to using JavaScript Promise, and it’s worth exploring both to determine which suits your needs. However, there are specific scenarios where opting for async/await is a better choice:

  1. Readability and Synchronous-Like Code: Async/await transforms complex asynchronous code into a clear sequence, mimicking regular step-by-step instructions. The async keyword marks functions that handle async tasks, while await halts execution until a task completes. This style makes intricate logic easier to understand.
  2. Flat Code Structure: Have you ever tried to navigate through a maze? Deeply nested code structures can be similarly challenging to navigate. Async/await rescues you from this “nesting maze.” By using async/await, you create a flatter code structure, avoiding the infamous “Callback Hell” or “Promise Hell.” Each step becomes a simple line of code, contributing to a more organized and maintainable codebase.
  3. Less Callback Nesting: Consider assembling a puzzle. When the pieces fit smoothly, it’s much easier to build the complete picture. Similarly, async/await lets you handle complex tasks with fewer layers of nested callbacks. This results in a cleaner execution flow, easier debugging, and improved maintainability.
  4. Modern codebase: async/await is a contemporary feature that’s widely supported in modern JavaScript environments, starting from ES2017 and onwards. If you’re working on a project that utilizes the latest JavaScript capabilities, async/await fits seamlessly into this modern context.

async/await provides a clearer, more structured approach to handling asynchronous operations. It enhances code readability, fosters a flatter and less nested code structure, and aligns well with modern JavaScript practices. While both async/await and Promises have their merits, async/await’s natural feel and modern compatibility make it a go-to choice for many developers seeking to handle asynchronous tasks efficiently.

For a more detailed understanding of JavaScript Promises and their use cases, you can refer to this article: Exploring JavaScript Promises.

How to use JavaScript Async/Await?

We can start by creating an async function. This async keyword means that the function will return a JavaScript Promise that will run as asynchronously. This is the syntax on how you can create an async function.

async function myAsyncFunction () {
    // The asynchronous code that will be executed.
}        

While async function ensures that the function returns a Promise, the await keyword wait until a declared Promise is finished and returns a result. You can only use await keyword inside async functions otherwise, it will return an error.

const myPromise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("Finished successfully");
    }, 2000);
});

async function myAsyncFunc() {
    const result = await myPromise;
    console.log(result);
    console.log("Moving on")
}

myAsyncFunc();        

As you can see, we created a promise that will resolve a string “Finished successfully” after a 2000ms delay. Then I declared that promise inside a function called myAsyncFunc that encapsulates the asynchronous operations. With the await keyword, the operations inside the myAsyncFunc will be paused until the result variable get the resolved value from myPromise. After getting the resolved value from the myPromise Promise, the process will run normally one by one starting from logging the value of the result variable and then logging the line “Moving on”.

Well, something like that was expected to happen, so let’s add a small section of code that will make a difference when you try to learn about asynchronous.

const myPromise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("Finished successfully");
    }, 2000);
});

async function myAsyncFunc() {
    const result = await myPromise;
    console.log(result);
    
    setTimeout(() => {
        console.log("This log will show last");
    }, 2000);
    
    console.log("Moving on")
}

myAsyncFunc();        

As you can see, we just added a similar code as the one inside myPromise into myAsyncFunc. What’s the effect and what’s the difference? By adding an await keyword before we call our Promise, it will “pauses” the execution of the async function until the Promise is resolved or at least until the operation is completed. This can give the appearance of synchronous code flow within an asynchronous context.

Can’t we add the await keyword before we call the setTimeout function? The answer is yes we can, but it won’t have any effect on the result, why? Because setTimeout is an asynchronous function that doesn’t return a Promise, so even if we try to add an await keyword, it won’t make any difference. Here’s the result of our code above.

As you can see in the result above, “This log will show last” is shown at the end of the logs, and “Moving on” is shown right after the “Finished successfully”. While the “Finished successfully” is treated synchronously inside the async function because of the await keyword, the “This log will show last” is not.

Here’s another example of using async/await to grab user data from GitHub from a given GitHub username and will print a log of that user data.

class HttpError extends Error {
   constructor(response) {
      super(`${response.status} for ${response.url}`);
      this.name = 'HttpError';
      this.response = response;
   }
}

async function loadJson(url) {
  const response = await fetch(url);
   if (response.status == 200) {
     return response.json();
   } else {
     throw new HttpError(response);
   }
}

async function demoGithubUser() {
   let name = prompt("Enter a name?", "denoland");
    try {
        const userJson = await loadJson(`https://api.github.com/users/${name}`);
        console.log(userJson);
        alert(`Github ID: ${userJson.id}, Github Name: ${userJson.login}.`);
        return userJson;
    } catch (e) {
      if (e instanceof HttpError && e.response.status == 404) {
          alert("No such user, please reenter.");
            return demoGithubUser();
        } else {
          throw e;
        }
    }
}

demoGithubUser();        

As we dive into JavaScript, we discover that handling actions that require waiting, like fetching data from the internet or responding to user actions, is crucial. To make this smoother, we have two tools in our toolbox: Promises and async/await. These tools help us execute tasks without blocking the entire program, keeping it efficient and responsive.

Picture async/await as a helpful guide in this journey. Imagine you’re cooking a multi-step recipe. When we mark a function as async, it becomes the chef of asynchronous tasks. When we use the await keyword inside this special chef function, it’s like waiting for each cooking step to finish before moving to the next. This avoids confusion and messy kitchens, much like handling multiple tasks in a neat order. By using async functions and the await keyword, we simplify our code, making it easier to read and understand.

In a nutshell, async/await acts as a translator between us and JavaScript’s asynchronous world. It turns complicated tasks into a smooth sequence of steps, improving how we handle complex processes. This way, even if our code is dealing with multiple tasks, it remains clear and logical, helping us create efficient and responsive programs.

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

社区洞察

其他会员也浏览了