Alex Devero Blog

Learn about development, and programming, especially in JavaScript, TypeScript and React, and design.

How JavaScript Async/Await Works and How to Use It

How JavaScript Async/Await Works and How to Use It feature image

Table of Contents

Promises made dealing with asynchronous code easier. ES8 introduced one feature that makes this even easier. This feature is async/await. This tutorial will help you learn about what async/await is and how it works. You will also learn how to use async/await to write asynchronous JavaScript.

Synchronous vs asynchronous code

JavaScript is a synchronous single-threaded programming language. What this means that it can perform only one operation at the time. When one operation is executed other operations are blocked and have to wait. They can be executed only when the currently executed operation is finished. This is also called blocking.

Now, what if code is asynchronous? It works in the opposite way. When asynchronous code is executed it doesn’t block other code. Other code can still be executed while the asynchronous operation is being executed. That asynchronous code is basically running in the background making space for other operations to take place.

You may not need to execute asynchronous operation all the time. However, there are situations when performing some operations asynchronously will be better, maybe even necessary. One example is fetching data from server. This may sound like something that is easy to do. Well, there is at least one problem.

When you fetch data from a server you never really know how fast you get them. Let’s say you fetch these data in synchronous way. This means that you are blocking the main thread . When this happens other operations has to wait until the fetching is done and the main thread is available to use.

This will not happen if you fetch these data in asynchronous way. If the response from server is not immediate it doesn’t block the main thread. In this case, your data fetching is moved to the siding until it is finished, metaphorically speaking. Any other code that needs to be executed can be executed right away.

It is only when that data fetching is complete, either with success or failure, when that operation moves to the main thread again. This doesn’t mean you should re-write all your synchronous code to asynchronous. All it means is that there are some situations when asynchronous code can be quite useful.

Async/await are one way to make writing and working with asynchronous code. Let’s take a look how it works and how you can use it.

Async functions

There are two fundamental building blocks of async/await. The first are async functions. Let’s take a look at how can you create a new async function.

The async keyword

The most important part of an async function is async keyword. This will tell JavaScript that you are want to declare an async function instead of regular. It is also this async keyword what will allow you to use await keyword inside that async function. Otherwise, JavaScript will throw SyntaxError. More about this later.

When you want to create an async you put the async keyword before the function keyword and its name, async function myAsyncFunc() {} . This is function declaration . In case of function expression the async keyword goes between the equal sign and function keyword, const myAsyncFunc = async function() {} . This is all you need to create an async function.

Returning a value from async function

Creating async functions is very similar creating a regular [functions]. One difference is the async keyword. Another, and more important, is that async functions always return a promise. This doesn’t mean that you should not use return statement inside async functions. You still can.

When you use return statement to return a value from an async function that function will still return resolved promise. The value of this promise will be the value you returned. You can also return resolved promise directly. To do this you can use Promise object and resolve() method, the value being passed as a parameter to resolve() .

This also means one thing. If a function returns a promise you have to handle that returned promise in the right way. This means using then() method to get and process the returned value from that promise. Since you are working with promise you can also use other handler functions , such as catch() and finally() .

The await keyword

The second fundamental building block of async/await is the await keyword. This keyword is inseparable from async functions. You can use await only inside an async function. You can’t use it outside it, yet . You also can’t use it inside regular functions. If you try it JavaScript will throw SyntaxError.

The await keyword tells JavaScript to pause the execution of the async function in which it is. This function is then paused until a promise, that follows this keyword, settles and returns some result. So, it is this await keyword what moves the executed code the siding until it is finished. In the meantime, other operations can take space in the main thread be executed.

Await and promise.then()

Notice one thing on the example above. You are creating a promise that resolves after 0.5s. Next, you are using await to invoke this promise, the messagePromise . At the same time, you are assigning the resolved promise to a variable messageResult . After that, you are logging the value of that variable.

There is one thing missing, one thing that should be there and it is not. This thing that is missing is the then() function. This function is supposed to get the value from the returned promise. Yet, the code still works. When you invoke the myAsyncFunction() function you will still see the message logged in console.

This is another thing await does for you. It replaces the then() function. When you use await to assign some resolved Promise to a variable it will automatically “extract” the resolved value. You don’t need to use then() . The work then() would do has been already done by await .

This is why you didn’t need to use then() function on messageResult variable. Yet, you still managed to get the message, the value returned by resolved promise. So, remember, when you use await to wait for resolved promise you don’t to use then() function.

Top-level await

At the time of writing this tutorial, it is not possible to use await keyword in a global scope. As you know, await keyword can be used only inside async function. One good news is that there as a proposal for top-level await . This proposal is at stage three so it might be too long until it is part of JavaScript.

Second good news is that you don’t have to wait for top-level await to happen. There is a workaround you can use today. What you can do is to create top-level async IIFE (Immediately-invoked function expression).

Since this function is async you can use await inside it. When top-level await is part of JavaScript specification you can remove the async IIFE and. Until then, it will do the job.

Async/await and error handling

When it comes to async/await and errors, there are two ways to deal with them. One way is by using catch() function. Async function returns a promise. When promise gets rejected it is catch() function what allows you to catch and handle this error. This works also for Async/await.

The second option is to use try...catch statement. In this case, you use try block to wrap the part of your code that contains await . Next, you use the catch block to handle any error that occurs.

Word of caution

As you know, await pauses execution of async function in which it is. This is good. It means you don’t have to worry about when your promise will be settled, resolved or rejected. However, this has some consequences. Since the await paused the async function this function can’t finish its execution until the promise is settled.

This may not be a problem if you await one promise and the response is fast. What if you await multiple promises? What if getting some responses takes more time than others? Then, the execution of that async function will also take more time. Let’s take a look at one example of an async function with three awaited promises.

As you can see, when the function waited for all promises to settle it took it around 2 seconds to execute the whole block. This is because all promises in the example above that are preceded by await keyword are executed in a sequence. So, when one awaited promise is being executed other promises that follow it has to wait.

It is only when the first one is settled the other can be executed. This applies to all awaited promises in the “chain”. The second has to wait for the first. The third has to wait for the second. This repeats until all awaited promises are settled. During this time the async function is paused with each await keyword.

Fortunately, there is a way to make this faster. You can run all those promises in parallel and await only the final result of those promises. To do that you can use Promise.all() method. This method accepts an iterable object of promises, like an array. When all promises are settled it returns one promise withe all values.

So, what you need to do is to take those promises and put them inside the Promise.all() . Then, instead of awaiting all those promises you will await only the Promise.all() .

As you can see, the updated myAsyncFunc() function ran almost twice as fast, thanks to Promise.all() method and running all promises in parallel. Remember this the next time you will want to use await and make you use it properly.

A real world example

You’ve learned a lot about async functions, await and asynchronous code. How about to put all this knowledge to practice? Let’s create a function that will fetch GitHub API and return data for one specific user. This function will be async. It will use JavaScript fetch() API to fetch the GitHub API and wait for the response.

When the response arrives, the async function will translate received data to JSON format and return the result. Since this is an async function the data will be returned in the form of a promise. To get the data from the resolved promise will need to use then() method. Then, we will log these data to console.

Now, use what you’ve learned today to do this exercise. If you get stuck, need a hint, or just want to compare your solution, take a look one possible solution in the example below.

Conclusion: How JavaScript Async/Await Works and How to Use It

Congratulations! You’ve just finished this tutorial on async/await. I hope you enjoyed it. Let’s do a quick recap. Today, you’ve learned that JavaScript is a synchronous single-threaded programming language and what is the difference between synchronous vs asynchronous code.

Next, you’ve learned what are async functions and how to create them. You’ve also learned about await keyword, how to use it and when. After that, you’ve learned about one workaround that will allow you to use await in a global scope (hint: IIFE), and how to use try...catch and catch() to handle errors.

As the last thing, you put your knowledge of async/await to test. You’ve created your own async function to fetch user data from GitHub API, await the response and return translated data.

Do you have any questions, recommendations, thoughts, advice or tip you would like to share with other readers of this blog, and me? Please share it in a comment. You can also send me a mail . I would love to hear from you.

If you liked this article, please subscribe so you don't miss any future post.

Also, you can find me on GitHub , Twitter and Dribbble .

If you'd like to support me and this blog, you can become a patron, or you can buy me a coffee 🙂

Buy Me A Coffee

By Alex Devero

I'm Founder/CEO of DEVERO Corporation. Entrepreneur, designer, developer. My mission and MTP is to accelerate the development of humankind through technology.

Leave a Reply Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed .

Stack Exchange Network

Stack Exchange network consists of 183 Q&A communities including Stack Overflow , the largest, most trusted online community for developers to learn, share their knowledge, and build their careers.

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

What does "Awaiting Assignment to Batch" mean?

My paper was accepted a week ago, at that time the status in the ScholarOne manuscript center changed from "accept" to "awaiting production checklist". They also said I would receive an email regarding the proof check. However, till today I haven't received any email. Today the status again changed to "Awaiting Assignment to Batch". I am curious what is this mean? According to the journal, it takes 2 weeks(on average) to get published "online pre-print" from the acceptance date. I am just worried if I am missing something.

  • publications
  • peer-review
  • journal-workflow

pritom's user avatar

First, congratulations!!

"Awaiting Assignment to Batch" is part of the accepted workflow. Once accepted, some journals have a set of tasks that a manuscript goes through. One of the steps available (and being used here) is batching and sending of the manuscript to the journals production service. It will wait until a predetermined condition (so many ready to go, or a regular timed interval), then be added to a large zip file (along with others in the "batch"), and sent to the production server.

Hope this helps!

NOTE: I am am employee of ScholarOne, but my opinions and answers are only my own.

Greg Mowery's user avatar

  • Thanks a lot. This is my first time submitting in a journal so I am just anxious. –  pritom Commented Dec 28, 2020 at 13:21

You must log in to answer this question.

Not the answer you're looking for browse other questions tagged publications peer-review journals journal-workflow ..

  • Featured on Meta
  • We've made changes to our Terms of Service & Privacy Policy - July 2024
  • Announcing a change to the data-dump process

Hot Network Questions

  • What techniques are there for preventing radio-signal replay attacks?
  • Please “weigh in” on my shelf weight question
  • What is the “Reinforcement Axiom” for a social choice function?
  • Is it correct to say: "To solve an addiction"?
  • parallelStream with collect method()
  • Borderlands 2 for PS3 has unbelievably low drop rates. Is something wrong?
  • How can Blowfish be resistant against differential cryptanalysis if it doesn't have S-boxes tuned for that?
  • When I use SSH tunneling, can I assume that the server does not need to be trusted?
  • Compact rotatable connection that supports pull forces?
  • embedding into hilbert cube.
  • Private sector professional being screened out of PhD program
  • How to reconcile different teachings of Jesus regarding self defense?
  • Trimming direction on climb out and descent to maintain an airspeed
  • Is it realistic that I can fit and maintain 22 trillion people without the need to eat in East Asia?
  • How to extract pipe remnant from ground sleeve?
  • What acceleration features did 2D PC video cards have? Comparing to video game consoles
  • TLS 1.3 key derivation and length of labels used (RFC 8446)
  • Does a router lookup in routing table twice for each packet?
  • IQ test puzzle with diagonal line of clocks
  • What would the correct answer be for Mendelson Exercise 1.4 (g)
  • Is there any airplane that doesn't use a headset but speakers?
  • How can DC charge a capacitor?
  • Solving a system of inequalities involving Binomial
  • Was the idea of foxes with many tails invented in anime, or is it a Japanese folk religion thing?

await job assignment

await job assignment

  • Latest Articles
  • Top Articles
  • Posting/Update Guidelines
  • Article Help Forum

await job assignment

  • View Unanswered Questions
  • View All Questions
  • View C# questions
  • View C++ questions
  • View Visual Basic questions
  • View Javascript questions
  • View .NET questions
  • CodeProject.AI Server
  • All Message Boards...
  • Running a Business
  • Sales / Marketing
  • Collaboration / Beta Testing
  • Work Issues
  • Design and Architecture
  • Artificial Intelligence
  • Internet of Things
  • ATL / WTL / STL
  • Managed C++/CLI
  • Objective-C and Swift
  • System Admin
  • Hosting and Servers
  • Linux Programming
  • .NET (Core and Framework)
  • Visual Basic
  • Web Development
  • Site Bugs / Suggestions
  • Spam and Abuse Watch
  • Competitions
  • The Insider Newsletter
  • The Daily Build Newsletter
  • Newsletter archive
  • CodeProject Stuff
  • Most Valuable Professionals
  • The Lounge  
  • The CodeProject Blog
  • Where I Am: Member Photos
  • The Insider News
  • The Weird & The Wonderful
  • What is 'CodeProject'?
  • General FAQ
  • Ask a Question
  • Bugs and Suggestions

await job assignment

Async/Await Explained with Diagrams and Examples

await job assignment

Introduction

This documentation explains how Async/Await works. It can be a confusing subject. We'll start out with some simple basic concepts and slowly work our way up to the more advanced concepts. Hopefully, the visual diagrams will help those who are visual learners.

The following discussion is primarily from a WPF point of view, though I do occasionally look at WinForms.

Nomenclature

Sync calling sync, async awaiting async, sync calling task.run(), async awaiting task.run(), async calling sync, sync calling async = ☠.

  • Returning a value

Passing Parameters

Completing on any thread, using cancellationtoken with task.run().

  • Getting Back To the UI Thread ( Message Queue , Message Loop , SynchronizationContext )

How Await Works

Async, method signatures, and interfaces, proper use of async/await, converting code to async.

  • Fixing Code that has Async Sprinkled In Various Places

We'll start out by defining some nomenclature.

Synchronous (Sync) Method

A synchronous ( sync ) method is a regular method which is not marked async and does not have an await in it. For example:

Asynchronous (Async) Method

An asynchronous ( async ) method is a method which is marked async and has an await in it. For example:

Asynchronous method names often end with “ …Async() ”.

An async method should return a Task . The only place it is OK to have an async method return void is in an event handler, such as a button click event handler:

Synchronous Call

A synchronous call is a call which does not include an await . It may or may not return a value. For example:

Asynchronous Call

An asynchronous call is a call in which await is used. It may or may not return a value. For example:

Note the await does not ‘launch’ the call to BarAsync() ; instead, the await determines what is done with the result of BarAsync(); which may be an incomplete Task or a completed Task.

An analogy would be the return in the following statement:

Here, we do not say the return ‘launches’ Bar(); instead, the return determines what is done with the result of Bar();

A Regular Call

a regular call

Foo() calls Bar() . Bar() runs and then returns to Foo() .

A Regular Call to stream.Read()

a regular call to stream.Read()

Foo() calls stream.Read() . The thread waits until stream.Read() completes, and then continues.

await stream.ReadAsync()

await stream.ReadAsync()

ButtonClick() calls await stream.ReadAsync() . Instead of waiting for the read to complete, the thread returns to the caller of ButtonClick() , allowing the thread to do other things.

Typically ButtonClick() is called from the UI thread's Message Loop (described below) and runs on the UI thread. By returning during the await, the UI thread is able to process other messages in the Message Queue and update the screen.

Later, when the stream.ReadAsync() completes, the rest of the ButtonClick() method runs on the UI thread (dark blue).

How we get the UI thread to run the rest of ButtonClick() after stream.ReadAsync() completes is a bit complicated and will be explained later.

Doing Two Things at Once with One Thread

In the above scenario, ButtonClick() calls await stream.ReadAsync() . Instead of waiting for the read to complete, the UI thread returns to the caller of ButtonClick() , allowing the UI thread to do other things.

At this point, it could be said we are doing two things simultaneously:

  • We are waiting for the ReadAsync() call to complete
  • The UI thread is processing messages in the Message Queue

It's a matter of sematics whether or not waiting actually counts as doing something (typically it does not count). We are indeed waiting, and we do have things set up so that when this wait completes the rest of the code after the await stream.ReadAsync() will execute; however, it is a passive wait, and we are not tying up the UI thread during this wait period. The UI thread is free to do other things while we also passively wait for the await stream.ReadAsync() to complete. Note there is still only one thread, the UI thread, and that thread is still doing all the work.

ButtonClick calling FooAsync calling stream.ReadAsync()

ButtonClick calling FooAsync calling stream.ReadAsync()

ButtonClick() calls await FooAsync() . FooAsync() calls await stream.ReadAsync() . Instead of waiting for the read to complete, an uncompleted Task is returned to the caller of FooAsync() . await FooAsync() sees the Task returned to it is incomplete, so it returns to its caller, which is the UI thread's Message Loop . This allows the UI thread to process other messages in the Message Queue and update the screen.

Later, when ReadAsync() completes, the rest of FooAsync() runs (dark blue). When FooAsync() reaches the end it returns a completed Task to await FooAsync() and the rest of ButtonClick() runs.

All of the action in this example happens on the UI thread. The UI thread is freed up while waiting for a read to complete. When the read is complete, the rest of FooAsync() runs on the UI thread, and when FooAsync() returns to ButtonClick() the rest of ButtonClick() runs on the UI thread. The details behind how this is achieved will be explained later below.

Sync → Task.Run() → Sync

Sync Task.Run() Sync

Foo() queues Bar() to run on a ThreadPool thread. Foo() continues executing without waiting for Bar() to complete. Bar() runs independently on a ThreadPool thread.

Sync → Task.Run() → Sync and Waiting

Sync → Task.Run() → Sync and waiting

  • Task.Run() queues Bar() to run on a ThreadPool thread.
  • The thread running Foo() enters a wait state by setting its execution state to “ WaitSleepJoin ” (the blocked state), and yields the remainder of its processor time slice. (This frees the CPU to run other threads.) The thread consumes no processor time until its blocking condition is satisfied.
  • Later, when Bar() completes, the thread running Foo() has its execution state set back to “ Running ,” and resumes running when the Thread Manager has a time slice available for it.

Performing a .Wait() on the UI thread is ill-advised as that can make the program unresponsive. We don’t want to be tying up the UI thread doing nothing. (Consider converting Foo() to an async method and using await Task.Run(()=>Bar()); instead.)

If instead Foo() is running on a ThreadPool thread, then performing a .Wait() is once again ill-advised, as now we are blocking a ThreadPool thread waiting for another ThreadPool thread to run Bar() . Why start another thread and then just wait for it to finish when you could be doing that work yourself?

This brings us to a general rule regarding waiting for Tasks to complete:

Avoid using Task.Wait and Task.Result

“There are very few ways to use Task.Result and Task.Wait correctly so the general advice is to completely avoid using them in your code.” David Fowler, Partner Software Architect at Microsoft — https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md

Async awaiting Task.Run() Launching Sync Method

Async awaiting Task.Run() launching Sync Method

  • FooAsync() queues Bar() to run on a ThreadPool thread and returns to its caller. (If FooAsync() is running on the UI thread, the UI thread is not blocked, which is good.)
  • Bar() runs ( red ).
  • When Bar() completes, the task running Bar() completes. FooAsync() then continues using the same SynchronizationContext it started on (blue). This means if FooAsync() was running on the UI thread then FooAsync() continues on the UI thread; if FooAsync() was running on a ThreadPool thread, then FooAsync() continues using any ThreadPool thread.

Note FooAsync() is not awaiting on method Bar() to complete, FooAsync() is awaiting on the task running Bar() to complete.

Async awaiting Task.Run() Launching Async Method

Async awaiting Task.Run() launching Async method

  • await Task.Run(async () => await BarAsync()) queues BarAsync() to run on a ThreadPool thread and returns to its caller. (If FooAsync() is running on the UI thread, the UI thread is not blocked, which is good.)
  • BarAsync() runs ( red ).
  • When BarAsync() comes to the await stream.ReadAsync(buffer) statement, instead of waiting for the read to complete, BarAsync() returns and the ThreadPool thread running BarAsync() is freed to run other tasks.
  • When stream.ReadAsync(buffer) completes, the rest of BarAsync() runs on any available ThreadPool thread.
  • When method BarAsync() completes, the task running BarAsync() completes, and the rest of FooAsync() continues using the same SynchronizationContext it started on ( blue ). This means if FooAsync() was running on the UI thread then FooAsync() continues on the UI thread; if FooAsync() was running on a ThreadPool thread, then the rest of FooAsync() continues using any ThreadPool thread.

Note FooAsync() is not awaiting on method BarAsync() to complete, FooAsync() is awaiting on the task running BarAsync() to complete.

Async calling Sync

In general, an asynchronous method can call a synchronous method. The asynchronous method just pretends for a moment that it's a synchronous method calling a synchronous method. For example, asynchronous code can call a simple synchronous function which multiples two numbers together and returns the result.

There are cases where using await to asynchronously call a synchronous method that returns a Task can get one into trouble. There are also cases where this is perfectly acceptable. It depends on details of the returned Task . This issue will be discussed further in a follow-up article on async / await .

The term “Sync over Async” refers to Synchronous code calling Asynchronous code. Synchronous code cannot await asynchronous code, therefore it’s difficult to know when the Asynchronous code has completed. Even worse, waiting for asynchronous code to complete can result in a deadlock in certain situations. This leads us to the general rule for synchronous code calling asynchronous code:

Sync over Async is bad. Don’t do it.

Below, we examine what perils can happen when we attempt to call Async code from Sync code.

Sync → Async

Sync calling Async

Non-async Foo() calls BarAsync() . When BarAsync() calls await stream.ReadAsync(buffer) , it returns to Foo() which continues executing.

Later, after stream.ReadAsync(buffer) completes, the rest of BarAsync() runs ( dark blue ).

Note we cannot await the call to BarAsync() because Foo() is a synchronous method which does not support await . We have no way of knowing when or if the rest of BarAsync() runs. It may never run and we’d never know it.

Sync → Async → .Wait() ☠

Warning: Can lead to deadlock.

Sync calling Async and waiting

  • ButtonClick() running on the UI thread calls BarAsync() .
  • BarAsync() calls await stream.ReadAsync(buffer) , which at some point returns an incomplete task, which is stored as Task t in ButtonClick() .
  • ButtonClick() then calls t.Wait() . The UI Thread is now tied up waiting for task t to complete.
  • Later, when ReadAsync completes, it queues the rest of BarAsync() to run on the UI thread. Unfortunately, ButtonClick() is tying up the UI thread waiting for BarAsync() to complete. This results in a deadlock: ButtonClick() is waiting for BarAsync() , and BarAsync() is waiting for ButtonClick() . Since ButtonClick() is blocking the UI thread, the entire program freezes and is unable to respond to keyboard keys or mouse clicks.

Note it’s possible that in some situations the code will not deadlock: if stream.ReadAsync() is replaced with Task.Delay(0) then the await will skip the time consuming “return an incomplete task to the caller” ordeal and just continue running. However, if that Task.Delay(0) is replaced with Task.Yield() then the code will always deadlock.

Let’s see what happens when we try to fix this by calling the async method with a Task.Run() .

Sync → Task.Run() → Async

Sync calling Task.Run() calling Async

ButtonClick() running on the UI thread creates task t running BarAsync() . ButtonClick() then continues on its way, never further checking task t .

Separately, task t runs BarAsync() on a ThreadPool thread. (Tasks started with Task.Run() run on ThreadPool theads.) When BarAsync() comes to the await stream.ReadAsync(buffer) it returns freeing the ThreadPool thread so that thread can work on something else.

Later, when the ReadAsync(buffer) completes, the rest of BarAsync() runs on any available ThreadPool thread. The reason it can complete on any available ThreadPool thread is because philosophically, all ThreadPool threads are the same. (A more technical explanation would be because ThreadPool threads have no SynchronizationContext , so the ‘default’ SynchronizationContext is used, which translates to “Any ThreadPool thread.”)

We still have the problem of not knowing when our async task completes. Let’s see what happens now if we introduce a .Wait() to wait for the task to complete.

UI Thread → Task.Run() → Async → .Wait()

UI thread Task.Run() Async .Wait

Non-async ButtonClick() running on the UI thread uses Task.Run() to create task t running BarAsync() . ButtonClick() then calls t.Wait() and waits for task t to complete, blocking the UI thread.

Meanwhile, task t runs BarAsync() on a ThreadPool thread. (Tasks started with Task.Run() run on ThreadPool theads.) When BarAsync() comes to the await stream.ReadAsync(buffer) , it returns freeing the ThreadPool thread to work on something else.

Later, when the ReadAsync(buffer) completes, the rest of BarAsync() runs on any available ThreadPool thread.

When method BarAsync completes, the task t running BarAsync completes, the t.Wait() completes, and the rest of ButtonClick continues to run on the UI thread.

Even though this won’t deadlock, it does tie up the UI thread with a .Wait() , making our program unresponsive to user input while we wait. It would be much better to convert ButtonClick() to an async method and await task t instead.

ThreadPool → Task.Run() → Async → .Wait ☠

ThreadPool Task.Run() Async .Wait deadlock

Assume Foo() is running on a ThreadPool thread. Foo() calls Task.Run() to create a task t running BarAsync() . Foo() then calls t.Wait() and waits for task t to complete, blocking the ThreadPool thread it’s running on.

Meanwhile, task t runs BarAsync() on another ThreadPool thread. (Tasks started with Task.Run() run on ThreadPool theads.) When BarAsync() comes to the await stream.ReadAsync(buffer) , it returns freeing the ThreadPool thread to work on something else.

ThreadPool Starvation

The potential problem here is Foo() is blocking a ThreadPool thread, and we need another ThreadPool thread to complete BarAsync() after the await . It’s possible to conjure up a scenario where we launch multiple instances of Foo() tying up ThreadPool threads until there are no more ThreadPool threads left. All the ThreadPool threads are blocked waiting for yet another ThreadPool thread to finish running BarAsync() .

At this point, the operating system sees the need for more ThreadPool threads, so it creates a new ThreadPool thread. This new ThreadPool thread might run the remainder of BarAsync() ; or, it might run another instance of Foo() . Which method the new ThreadPool thread runs depends on the program details and how the ThreadPool queues are managed. If the new ThreadPool thread always runs the remainder of BarAsync() the system will start to recover; however, if the new ThreadPool thread instead always runs another instance of Foo() , then we are doomed: Foo() will block the new ThreadPool thread and we will be back to our ThreadPool starvation state, except the ThreadPool will have increased in size. The system may never recover, with the ThreadPool slowly increasing in size indefinitely, with all the ThreadPool threads blocked, each one forever waiting for just one more ThreadPool thread to come save it.

An example of this type of ThreadPool starvation is at this link .

Returning a Value

Sync calling sync returning value.

Sync calling Sync returning value

Async Awaiting Async Returning Value

Async awaiting Async returning value

This is the normal way of calling an async method:

  • FooAsync() calls BarAsync()
  • BarAsync() encounters the await Task.Delay(2000); and returns an incomplete task to FooAsync() , which returns the incomplete task to its caller.
  • Later, BarAsync() completes and returns 7 to FooAsync() which stores the 7 in variable x .
  • FooAsync() continues running now that it has a value for x .

Task.Run() Returning Value

Sync calling task.run() waiting on sync method returning value.

Foo() launches task, waits for task to complete, gets result from task.

Async awaiting Task.Run() returning value

Int x = await task.run on sync method returning value.

int x = await Task.Run on non-async method returning value

This is a standard way of awaiting on time consuming synchronous code.

  • Bar() runs (red).
  • When Bar() completes, the task running Bar() completes, and 7 is stored in variable x .
  • FooAsync() then continues: If FooAsync() was running on the UI thread, then FooAsync() continues on the UI thread; if FooAsync() was running on a ThreadPool thread, then FooAsync() continues on any ThreadPool thread.

int x = await Task.Run on async method returning value

int x = await Task.Run on async method returning value

  • int x = await Task.Run(async () => await BarAsync()) queues BarAsync() to run on a PoolThread thread and returns to its caller. (If FooAsync() is running on the UI thread, the UI thread is not blocked, which is good.)
  • When method BarAsync() completes, the task running BarAsync() completes, and 7 is stored in variable x.
  • FooAsync() then continues: If FooAsync() was running on the UI thread then FooAsync() continues on the UI thread; if FooAsync() was running on a ThreadPool thread, then FooAsync() continues on any available ThreadPool thread.

In this case, one may consider removing the Task.Run part and just using int x = await BarAsync();

await BarAsync() vs. await Task.Run(async () => await BarAsync())

Runs BarAsync() directly. If BarAsync() takes some time and does not await on time consuming code (e.g., perhaps it’s doing some CPU intensive calculation, so it can’t await that because it’s not waiting, it’s working), then the caller must wait while BarAsync() does it’s time consuming work.

Here, we are awaiting on the Task which is running BarAsync . This frees the caller to do other things while it awaits for the Task to complete. The task runs on a background ThreadPool thread.

Sync Calling Async Returning Value

Can’t be done because BarAsync() returns a Task<int> , not an int .

The caller needs to be an async method.

Sync Calling Task.Run() Launching Async Task and Waiting for Return Value

Can lead to Deadlock! (See Threadpool Starvation )

Don’t .Wait() on an async task. Instead, use await for an async task (or remove the Task.Run() and just await the method).

Async Calling Sync Returning Value

int x = Bar();

Async calling non-async returning value

This is generally OK as long as Bar() does not return a Task which we later wait on to complete. Sometimes, that's OK and sometimes that can lead to trouble. The details of that case will be discussed in a separate follow-up article on async/await.

One could also do:

though in this case one could just omit the Task.Run and do:

Don’t wait on an async method or task.

as that can lead to a threadpool starvation deadlock. (See ThreadPool Starvation .)

Completing on Any Thread

Adding .ConfigureAwait(false) allows the continuation after the await to run on any available thread ( dark red ). Typically, this will be a ThreadPool thread. This is convenient when we know the rest of ButtonClick() doesn’t need to be run on the UI thread.

Two Threads Not Doing Two Things at Once

In the above example, we have an instance of two threads running due to the .ConfigureAwait(false) which allows the remainder of ButtonClick() to run on a background ThreadPool thread, while the UI thread which started running ButtonClick() is free to do other things. Yet we are not doing two things at once, because even though the rest of ButtonClick() runs on a different thread, it still does not run until after the call to ReadAsync(buffer) completes. We're never doing two things at once, even though we're using two threads.

Async Calling Async Completing on Any Thread

Async Calling Async Completing on Any Thread

  • ButtonClick() starts on the UI thread.
  • ButtonClick() calls await FooAsync() .
  • FooAsync() calls await stream.ReadAsync() .
  • stream.ReadAsync() returns an incomplete task to ButtonClick() , which in turn returns to its caller (the message loop ).
  • When stream.ReadAsync() completes, the rest of FooAsync() runs on any available thread, likely a ThreadPool thread due to the .ConfigureAwait(false); (which is just a shorter way of saying .ConfigureAwait(continueOnCapturedContext:false); ).
  • FooAsync() completes and returns to ButtonClick() .
  • The rest of ButtonClick() runs on the UI thread (because the call to FooAsync() does not have .ConfigureAwait(false); appended).

Since I have numerous examples of using Task.Run() , I should include an example of using a CancellationToken with Task.Run() which is always a good idea.

From Microsoft documentation:

“When the owning object calls CancellationTokenSource.Cancel() , the IsCancellationRequested property on every copy of the cancellation token is set to true .”

Note how the cancellation token is passed twice to Task.Run() .

  • It’s passed as a parameter to SomeTask(cts. Token ) Passing the cancellation token to SomeTask() allows SomeTask() to periodically check the state of the Token to see if it’s been set to the “ Cancel ” state so it can abort the procedure if it has.
  • It’s passed as the 2 nd parameter to Task.Run(…, cts. Token ); Passing the cancellation token as the 2 nd parameter to Task.Run() allows Task.Run() to skip running SomeTask() if the Cancellation Token is already set to “ Cancel ” when Task.Run() is called.

Note that Microsoft developer Stephen Toub feels Tasks do not need to be disposed of so the above code which disposes of the Task may be overkill. See: https://devblogs.microsoft.com/pfxteam/do-i-need-to-dispose-of-tasks/

Getting Back to the UI Thread

In order to understand how await gets back to the UI thread when it needs to, we need to explain the concepts of the Message Queue , the Message Loop , and what a SynchronizationContext is.

The Message Queue

Windows programs that have a GUI (Graphical User Interface) have an individual message queue for each thread that has created a window. Usually, only the initial thread creates windows and maintains the message queue for the program. This thread is known as the “ UI Thread ” (User Interface thread) or GUI Thread (Graphical User Interface thread). (They’re the same thing.)

Events, such as button clicks or keyboard keys, get placed into this message queue .

Here is a visual diagram of a message queue . This is known as a “First In First Out” (FIFO) queue. Messages are retrieved in the order they go in. Imagine that each blue rectangle is a ‘message’, such as a Button Click event message, or a “ Keyboard Key was Pressed Down ” message.

Message Queue

Events which may be placed in the message queue include:

  • WM_LBUTTONDBLCLK
  • WM_MOUSEMOVE

(Search for file WinUser.h to see the full list of WM_* messages.)

The Windows operating system is responsible for sending event messages to the message queue. It determines which window on the desktop has focus, and sends the messages to the message queue associated with the thread that created that window.

More on Messages and Message Queues can be found here: https://docs.microsoft.com/en-us/windows/win32/winmsg/about-messages-and-message-queues

Posting a Message

Enqueueing a message to the message queue is known as “Posting” the message.

A message is added to the message queue by calling the operating system’s PostMessage() library routine.

The Message Loop

The UI thread runs code known as the message loop (also known as a message pump ). This is the code which removes messages from the message queue and processes them. The code is an endless loop which runs for the duration of the program and looks something like this:

If there is no message in the message queue, GetMessage() blocks; that is, GetMessage() waits until there is a message to return and then returns the message.

This endless loop doesn't exit until the program is requested to terminate.

PowerPoint Presentation on Windows and Messages : https://www.slideserve.com/finn/windows-and-messages

Caveat: The WM_TIMER Message

WM_TIMER messages are handled in a special way that is not "First In First Out" (FIFO). The SetTimer system function sets up a system timer with a counter containing the appropriate number of “ticks” until the timer expires. On every hardware timer tick, Windows decrements the counter of the timer. When the counter reaches 0, Windows sets a timer expired flag in the appropriate application’s message queue header. When the message loop calls GetMessage , if the message queue is empty, it then checks the timer expired flag, and if it is set, the function will return a WM_TIMER message and reset the flag. This means a WM_TIMER message may be delayed if the CPU is busy and there is a delay in emptying the message queue. It also means multiple WM_TIMER messages can't "pile up." Multiple timers may expire, setting the timer expired flag multiple times, yet only one WM_TIMER message will be generated before resetting the flag. It's also possible GetMessage could return a WM_TIMER message just before another time period expires, and, subsequently, return a second WM_TIMER message almost immediately after the first.

The Message Loop in WPF

Every WPF program has a Main() entry point which one can view by looking under App.xaml in Visual Studio's Solution Explorer and selecting Main() :

Main App.Run() is message loop

The code may be explicit, or in this case, it's implicit, auto-generated (hence the ' g ' in the file name App.g.i.cs ).

The UI thread is the thread which runs this Main() method. Main() creates an instance of class App which in turn specifies window MainWindow is to be created.

Startup URI embedded in App.xaml

Method Main() then calls app.InitializeComponent(); and lastly calls app.Run(); This is where the message loop is. Method Main() does not return until the program exists. It spends its life as the UI thread running the message loop .

Class App inherits from System.Windows.Application . The code for System.Windows.Application can be viewed at this link .

Find the Run() method there and trace it to the message loop .

Run → RunInternal → RunDispatcher → Dispatcher.Run() → PushFrame → dispatcher.PushFrame → message loop

In WinForms, a background thread can launch code to be run on the UI thread by using:

where delegate is the code we want to execute on the UI thread. This posts the delegate to the message queue of the thread that created the control (typically the UI thread).

In WPF, a background thread can launch code to be run on the UI thread by using:

where delegate is the code we want to execute on the UI thread. The delegate is posted to the Dispatcher where it eventually gets run. (A Dispatcher is the combination of a thread and a message queue . Typically, the thread is the UI thread for the program.)

SynchronizationContext

This is how we determine if we are on the UI thread or not.

A SynchronizationContext allows the code to get back to the UI thread if we were running on the UI thread when we entered the await.

In WPF, there are two versions of SynchronzationContext :

  • SynchronizationContext has a method Post() that queues methods to run on a ThreadPool thread. (This base class is actually not used. It's probably leftover from an earlier design iteration.)
  • DispatcherSynchronizationContext inherits from SynchronizationContext and overrides Post() with a method that queues methods to run on the UI thread.

To obtain a SynchronizationContext , one calls SynchronizationContext.Current . For example:

If we are currently on the UI thread, then for WPF projects, this returns an instance of DispatcherSynchronizationContext . (Similarly, for WinForms projects this returns an instance of WindowsFormsSynchronizationContext which also inherits from SynchronizationContext .) On the other hand, if we are on a ThreadPool thread, then SynchronizationContext.Current returns null .

The code for DispatcherSynchronizationContext can be seen at this link .

The Post() method for DispatcherSynchronizationContext is:

The constructor of DispatcherSynchronizationContext sets _dispatcher = Dispatcher.CurrentDispatcher

Now that we've discussed how the UI thread handles the message queue and message loop , and how a SynchronizationContext gets us back to the UI thread, we can now answer the question of how await works.

When we enter an await , if we're on the UI thread, then we want to later resume on the UI thread after the await . On the other hand, if we're on a ThreadPool thread when we enter the await , then we want to resume on a ThreadPool thread after the await .

Now consider the following code:

Here, ButtonClick() is called from the UI thread. The method first calls ReadAsync() to fetch some data into this.buffer . This read takes a long time, so ReadAsync() returns an incomplete Task to await which in turn returns to the caller of ButtonClick() , which is the message loop . This frees up the UI thread to process other messages in the message loop .

Later, after the ReadAsync() completes, we want to resume running the rest of ButtonClick() on the UI thread.

To accomplish this, when the compiler encounters the await keyword, it generates code before the call to stream.ReadAsync(this.buffer) which captures the SynchronizationContext . The code looks something like this:

At runtime, if we're on the UI thread, then sc becomes an instance of DispatcherSynchronizationContext (which inherits from SynchronizationContext so it is a SynchronizationContext ); otherwise, if we're on a ThreadPool thread then sc gets set to null .

After the await , the compiler generates code that looks something like this:

This can be interpreted as, "If we don't have a SynchronizationContext , then just run the rest of the code using whatever thread we happen to be on; otherwise, use the SynchronizationContext to get us back to the UI thread."

How the Rest of Await Works

There's still a bit more about the await keyword which I have not explained here. When the compiler encounters the await keyword, it creates a state machine to handle all the details of breaking up the code into sections: the section before the await , and the section after the await. This gets a bit messy, as one can imagine, and I am quite happy to let the compiler handle those details. There are many excellent articles on the internet which explain the details behind this state machine if the reader is interested.

(From the book Async in C# 5.0 by Alex Davies)

https://www.oreilly.com/library/view/async-in-c/9781449337155/ch04.html

The async keyword appears in the declaration of a method, just like the public or static keywords do. Despite that, async is not part of the signature of the method, in terms of overriding other methods, implementing interfaces, or being called. The only effect that the async keyword has is on the compilation of the method to which it is applied, unlike the other keywords that are applied to a method, which change how it interacts with the outside world. Because of this, the rules around overriding methods and implementing interfaces completely ignore the async keyword. C# class BaseClass { public virtual async Task<int> AlexsMethod() { ... } } class SubClass : BaseClass { // This overrides AlexsMethod above public override Task<int> AlexsMethod() { ... } } Interfaces can’t use async in a method declaration, simply because there is no need. If an interface requires that a method returns Task , the implementation may choose to use async , but whether it does or not is a choice for the implementing method. The interface doesn’t need to specify whether to use async or not.

The one issue with a method returning a Task is sometimes the method expects that Task to be waited on via a synchronous task.Wait(); , while other times the method expects the Task to be waited on via an asynchronous await task; . Using the wrong type of wait on the task can lead to deadlocks and other problems. This will be discussed further in a separate follow-up article.

Don't block on async code. (See: https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html )

A method with Async in its name should have await in front of it when called.

An async method eventually needs to call an async I/O routine.

Starting with the .NET Framework 4.5, the I/O types include async methods to simplify asynchronous operations. An async method contains Async in its name, such as ReadAsync() , WriteAsync() , CopyToAsync() , FlushAsync() , ReadLineAsync() , and ReadToEndAsync() . These async methods are implemented on stream classes, such as Stream , FileStream , and MemoryStream , and on classes that are used for reading from or writing to streams, such TextReader and TextWriter .

Efforts to be async amount to nothing unless the entire callstack is async. ( Async methods all the way, starting with the async button click event handler, going all the way down to the async I/O system call.)

If there are no natively async methods in the code, there is no reason to convert anything to async , you’ll just end up with “async over sync” or “sync over async” somewhere in the code. That is, somewhere an async method will call a sync method, or a sync method will call an async method.

Also, don't mix List<T>.ForEach() with async methods (or indeed Parallel.ForEach which has exactly the same problem). See: C# Async Antipatterns: Antipattern #5: Mixing ForEach with async methods , at: https://markheath.net/post/async-antipatterns

  • Identify a native I/O call that can be changed to an Async I/O call. For example, Read() → ReadAsync() .
  • Convert the native I/O call to an Async I/O call. [e.g. convert Read() → ReadAsync() .] Designate the method as an async method.
  • All methods calling this async method now need to be converted to async methods. (The compiler is your friend. Let it tell you what needs fixing.)
  • Repeat that last step until there are no more methods that need converting to async methods. All methods up the call chain will be converted to async methods until reaching an event handler, such as a button click event handler.

Don’t leave a synchronous method calling an asynchronous method. (Sync over async.)

Fixing Code that Already has Async Sprinkled In Various Places

Do the async methods eventually call a native async I/O method, such as ReadAsync or WriteAsync ? Are the async methods called by async methods all the way up the call chain to an event handler? Is there any instance of a synchronous method calling an asynchronous method ("Sync over Async")? If there is, can the sync methods be changed to async methods up the call chain? Or, perhaps the async methods aren't neccessary and can be converted to synchronous methods?

  • Asynchronous Programming Guidance – David Fowler, Partner Software Architect at Microsoft https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md
  • Processes, Threads, and Jobs in the Windows Operating System by Kate Chase and Mark E. Russinovich 6/17/2009 https://www.microsoftpressstore.com/articles/article.aspx?p=2233328
  • Windows Internals, Part 1 (Developer Reference) by Pavel Yosifovich, Mark E. Russinovich, et al. | May 15, 2017
  • Windows Internals, Part 2 (7th Edition) (Developer Reference) by Mark E. Russinovich, Andrea Allievi, et al. | Jul 16, 2020
  • 9 th April, 2021: Initial version
  • 15 th April, 2021: corrected "the rest of FooAsync runs" → "the rest of ButtonClick runs"

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

await job assignment

Comments and Discussions

to use this message board.
  Layout   Per page    
First Prev Next
16-Jan-23 18:05 16-Jan-23 18:05 
Thank you for the super detailed explanation. It answered questions that I didn't even think of yet.

Perhaps in the following sample code fragment, the statement should be replaced with , since takes you out of the loop? (frame.Continue) { if (!GetMessage(ref msg, IntPtr.Zero, 0, 0)) ; TranslateAndDispatchMessage(ref msg); }
·  
6-May-23 8:50 6-May-23 8:50 
I checked with referencesource.microsoft.com and they use a break;
·  
13-Oct-21 22:39 13-Oct-21 22:39 
God bless you. Simple bite-size pieces that make perfect sense. Diagrams help immensely. AND a publish date that shows this technology is still (probably) a best practice. Thank you.
·  
20-Apr-21 10:02 20-Apr-21 10:02 
Thanks for this excellent and clear article. Will have to read it several times to let it sink in.

What most of the similar articles do not address is, what happens if the event handler awaits, but the user closes the form before the await completes. Normally in the sync case the event handler has to complete before the form can close. The form is marked for disposal. At least that is how I understand it. Is the disposal in this case suppressed, and the event handler does execute to completion in the background without it being visible?

Can you provide some clarity to this?

In the same vein, let's say I have a console app executing an async HTTP GET and I close the app before the await completes. What happens?

Thanks for all the effort!
·  
6-May-23 10:59 6-May-23 10:59 
My current understanding is, let's say our program's main thread comes to an await Task.Delay(5 hours); The main thread then returns to the message queue. Later the user clicks the red X in the upper right corner to close the main window and terminate the program. The process shutdown is initiated, everything is disposed of, including our 5 hour timer, as well as the instructions what to do when that timer expires. The code after the await never runs, because the entire program is terminated and disposed of.
·  
19-Apr-21 5:32 19-Apr-21 5:32 

·  
19-Apr-21 2:00 19-Apr-21 2:00 
Well written and even better explained!
·  
14-Apr-21 0:15 14-Apr-21 0:15 
At the section "Converting Code to Async" you give some hints how to deal with the case when waiting for IO is the bottleneck.

What is the best choice dealing when dealing with one computing intensive (sync) procedure Compute_A_Lot(), I am a little overwhelmed by the options. I used for WPF the old Backgroundworker to keep the UI alive in this case.

Sorry for the beginner question, may be this is not the right place to ask this question, but I thought who knows better then the expert.
·  
15-Apr-21 18:45 15-Apr-21 18:45 
For a CPU intensive operation, if it doesn't update the UI then see if it can be run on a background ThreadPool thread. Try the "Async Awaiting Task.Run()" section. Use Task.Run() to run the method that takes a bit of time to run.

A common problem I've encountered is where a method buried deep in the code directly updates the UI. In this case it may be necessary to manually marshal the UI update onto the UI thread by enclosing it in a method (MyUpdateUIMethod) and then posting that method to run on the UI thread:



If that doesn't work (sometimes the code at this point is so detached it doesn't know what the is. This can happen if it was launched using for example), then another possibility is when I know I'm on the UI thread, get a SynchronizationContext:



and then pass to the method that wants to update the UI. (Because that SynchronizationContext will know how to get back to the UI thread.) Then I can call:



For example, on the UI thread:

Task.Run(SomeMethod(sc));

Then in SomeMethod:

void SomeMethod(SynchronizationContext sc) { sc.Post(MyUpdateUIMethod); }

This is just a band-aid temporary fix. Hopefully the code can later be refactored to get all the UI stuff into a "View" part of a "Model-View-ViewModel" configuration. This brings up the topic of Refactoring which is a whole topic in itself. (Maybe someday I'll do an article on Refactoring. There's a new book coming out called "Five Lines" which is pretty good about Refactoring. I ordered the preview and was able to read the first several chapeters which are already written.)
·  
16-Apr-21 7:09 16-Apr-21 7:09 
Thank you for your answer, next time I will take a look at it.

B.t.w. when I recently used the backgroundworker I saw in the Windows Community Toolkit Sample app a sample of how to use the UI thread from another thread using DispatcherQueueExtensions but at first glance that works for only for UWP for now.

From a UI thread, capture the DispatcherQueue once: var dispatcherQueue = DispatcherQueue.GetForCurrentThread(); // The use it from any other thread int crossThreadReturnedValue = await Task.Run<int>( async () => { // Task.Run() will guarantee the given piece of code be executed on a separate thread pool. // This is used to simulate the scenario of updating the UI element from a different thread. int returnedFromUIThread = await dispatcherQueue.ExecuteOnUIThreadAsync<int>(() => { NormalTextBlock.Text = "Updated from a random thread!"; return 1; }); //ExecuteOnUIThreadAsync was called from the non-UI thread, execute the given UI-update lambda in UI thread , and return the value 1 back to the caller (non-UI) thread. return returnedFromUIThread + 1; });
·  
12-Apr-21 10:05 12-Apr-21 10:05 
Very nice article David! As a visual learner I like your use of diagrams to reinforce the text!
This is a good subject to get well versed in.
·  
12-Apr-21 8:04 12-Apr-21 8:04 
Love the diagrams, the content, wish I could vote 5x5.

·  
12-Apr-21 0:26 12-Apr-21 0:26 
In the section "Sync Calling Sync - ButtonClick calling FooAsync calling stream.ReadAsync()" you wrote: When FooAsync() reaches the end it returns a completed Task to await FooAsync() and the rest of FooAsync() runs. I would have assumed that the rest of the UI thread / ButtonClick method runs?!
·  
15-Apr-21 18:08 15-Apr-21 18:08 
Yes that should say "the rest of ButtonClick() runs." Thank you for pointing that out I'll get that fixed.
·  
10-Apr-21 19:05 10-Apr-21 19:05 
Great explanation! Thanks.
·  
Last Visit: 31-Dec-99 18:00     Last Update: 4-Aug-24 10:33

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

await job assignment

DEV Community

DEV Community

Guchu Kelvin

Posted on Jun 16, 2023

Mastering Asynchronous JavaScript: Promises, async/await, Error Handling, and More

Introduction.

Hello, let's familiarize ourselves with asynchronous JavaScript, where the boundaries of execution are pushed beyond traditional synchronous programming. In this comprehensive guide, we explore the depths of Promises, async/await syntax, error handling, parallel execution, and common asynchronous patterns.

As web applications grow in complexity, efficiently handling asynchronous tasks is crucial. Whether you're a curious novice or an experienced developer seeking to optimize performance, this guide has something valuable for you.

Discover the elegance of async/await syntax, where complex asynchronous code reads like synchronous code. Explore the power of Promises and learn to seamlessly chain tasks. I will provide practical examples, expert insights, and real-world case studies to demystify complexities and empower you to write clean, scalable asynchronous code.

What is Asynchronous JavaScript:

Asynchronous JavaScript is a programming paradigm that enables the execution of multiple tasks simultaneously without blocking the normal flow of a program. Unlike traditional synchronous programming, where operations are performed one after another sequentially, asynchronous JavaScript allows developers to initiate time-consuming tasks and continue executing other operations while waiting for those tasks to complete.

At the core of asynchronous JavaScript are concepts such as Promises, async/await syntax, and callback functions. Promises act as placeholders for future results, facilitating the handling of success or failure outcomes. The async/await syntax provides a more readable and structured way to write asynchronous code, resembling the familiar style of synchronous programming. Callback functions are used to define actions that will be executed once an asynchronous task completes. Let's dive right in!!!

Introduction to Asynchronous Programming:

Explanation of synchronous vs. asynchronous execution;.

In the world of JavaScript programming, understanding the difference between synchronous and asynchronous execution is key to writing efficient and responsive code. Imagine a symphony orchestra playing a beautiful piece of music. Synchronous execution is like each musician playing their part in perfect harmony, following a predefined order. On the other hand, asynchronous execution is akin to a group of musicians playing their individual instruments at their own pace, seamlessly blending together to create a symphony.

In synchronous execution, tasks are performed one after another sequentially. Each task must complete before the next one can begin. It's like a step-by-step process, where the program waits patiently for each task to finish before moving on to the next. While this approach ensures predictability and simplicity, it can also introduce delays, especially when dealing with time-consuming operations. If one task takes a significant amount of time, it can cause the entire program to stall, leading to a less responsive user experience.

On the other hand, asynchronous execution introduces flexibility and efficiency by allowing multiple tasks to be initiated simultaneously. Instead of waiting for a task to complete, the program can continue executing other operations while keeping an eye on the progress of the asynchronous task. It's like a conductor guiding the musicians, allowing them to play their parts independently, resulting in a harmonious blend of sounds. Asynchronous execution is particularly useful when dealing with operations that involve waiting for external resources, such as fetching data from a server or handling user interactions.

To achieve asynchronous execution, JavaScript employs various techniques, such as callbacks, Promises, and async/await syntax. These mechanisms provide ways to handle and coordinate asynchronous tasks effectively. With callbacks, functions are passed as arguments and invoked when a task completes. Promises, on the other hand, provide a cleaner and more structured approach to managing asynchronous operations, enabling better error handling and chaining of tasks. The newer async/await syntax offers a more intuitive way to write asynchronous code that resembles synchronous code, making it easier to understand and maintain.

Callback Functions:

Understanding callback functions and their role in asynchronous operations;.

Callback functions play a vital role in orchestrating the seamless flow of operations. Imagine a gathering of friends, each bringing their unique talents to create a delightful party. In a similar way, callback functions bring together different parts of your code, ensuring that tasks are executed at the right time, creating a harmonious dance of asynchronous operations.

At its core, a callback function is a piece of code that is passed as an argument to another function. It is like a musical cue, telling the program what to do when a specific task is completed. Just as one friend may call out, "Let's dance!" to trigger a joyful response from the group, a callback function is invoked when a particular operation finishes its work.

Callback functions excel in asynchronous scenarios, where time-consuming tasks need to be handled without disrupting the overall program flow. Instead of waiting for a task to complete before moving on, callback functions allow the program to continue its execution and perform other operations. Once the task finishes, the callback function is called, and it gracefully takes over, guiding the program towards the next steps.

An essential aspect of callback functions is their ability to handle the results of asynchronous operations. They embrace the concept of "don't call us, we'll call you." When an asynchronous task completes, it signals the program by invoking the associated callback function, passing any relevant data as parameters. This handover of control ensures that your code can respond appropriately to the completion of tasks, whether it's processing the retrieved data, updating the user interface, or triggering subsequent operations.

In JavaScript, callback functions shine in various contexts, such as event handling, timers, and network requests. They provide a way to respond to user interactions, wait for timeouts to elapse, or handle the retrieval of data from a server. With callback functions, developers can weave together complex asynchronous operations, allowing their code to gracefully adapt to dynamic situations.

While callback functions are a fundamental building block of asynchronous JavaScript, they can sometimes lead to callback hell—a situation where nested callbacks make the code difficult to read and maintain. To mitigate this, modern JavaScript introduces alternatives like Promises and async/await syntax, which provide more structured and readable approaches to managing asynchronous operations. However, understanding the concept and usage of callback functions is still valuable, as they form the foundation upon which these newer approaches are built.

Basic structure of a callback function:

In the above structure, the callback function takes two parameters: error and result . The error parameter is used to handle any error that might occur during the asynchronous operation, while the result parameter holds the data or outcome of the operation.

Inside the callback function, you can check if an error occurred by evaluating the error parameter. If error is truthy, it means an error occurred, and you can handle it accordingly. If error is falsy, it means the operation was successful, and you can work with the result data.

Note that, this is a basic structure, and the actual implementation of the callback function can vary depending on the specific use case and the asynchronous operation you are working with.

Examples of using callbacks to handle asynchronous tasks:

Fetching Data from an API: Consider a scenario where you need to fetch data from an API and perform an action once the data is retrieved. You can achieve this using a callback function. Let's dive into the code:

In this example, we define the fetchData function that takes a URL and a callback function as parameters. Inside fetchData , we simulate an asynchronous API call using setTimeout . Once the data is retrieved, we invoke the callback function, passing the retrieved data as a parameter. Here, the processData function acts as the callback, receiving the data and performing further actions.

Handling File Operations: Let's explore another example where you want to read data from a file asynchronously and log the content once the operation is complete:

Here, the readFileAsync function simulates reading a file asynchronously using setTimeout . Once the operation completes, the callback function is invoked with two parameters: error (if any) and content (the file content). The logContent function serves as the callback, logging the content if no error occurs or displaying an error message if an error is encountered.

By leveraging callback functions, JavaScript gracefully handles these asynchronous tasks. They ensure that the appropriate actions are taken when operations complete, allowing your code to flow harmoniously and deliver responsive applications. Keep in mind that while callbacks are a powerful tool, nesting them excessively can lead to complex and hard-to-maintain code, so it's essential to explore other techniques like Promises and async/await for more structured approaches to asynchronous programming.

Introducing Promises:

Introduction to promises and their purpose in managing asynchronous operations;.

Promises offer a structured and reliable way to handle asynchronous tasks. Think of Promises as trustworthy messengers who assure us that our asynchronous operations will be completed successfully. They provide a neat solution to the challenges of working with time-consuming tasks, allowing us to handle the results with ease.

A Promise represents the future completion or failure of an asynchronous operation. It serves as a placeholder for the eventual value we expect to receive. With Promises, we can focus on the logical flow of our code without worrying about the timing and execution of asynchronous tasks.

One of the benefits of Promises is their ability to gracefully handle both successful outcomes and errors. When a Promise is fulfilled, it means the task completed successfully, and we can access the resolved value it holds. On the other hand, if an error occurs, the Promise is rejected, enabling us to handle the failure gracefully.

Promises can be chained together, allowing for a streamlined sequence of asynchronous operations. Each Promise in the chain waits for the previous one to resolve or reject, ensuring that tasks are executed in the desired order. This structured approach simplifies our code, making it more readable and maintainable.

Creating and consuming Promises:

Promises opens up a world of efficient programming. Imagine the serene flow of a river, where you can navigate through tasks seamlessly, knowing that each step will be completed in due time. Promises provide the foundation for handling asynchronous operations, allowing you to create and consume them with ease.

To create a Promise, you encapsulate an asynchronous task within a Promise constructor. This task could be anything that takes time to complete, such as fetching data from a server or reading a file. Once the task is done, the Promise is either fulfilled with the result or rejected with an error.

Consuming a Promise involves attaching callbacks to handle the fulfillment or rejection of the Promise. These callbacks, known as then and catch , allow you to gracefully respond to the Promise's outcome. The then callback is executed when the Promise is fulfilled, enabling you to access the resolved value and perform further actions. On the other hand, the catch callback is triggered when the Promise is rejected, giving you the opportunity to handle errors and take appropriate measures.

The basic structure of a Promise:

The creation of a Promise involves passing a function (often referred to as an executor function) to the Promise constructor ( new Promise() ). This function takes two parameters: resolve and reject .

Inside the executor function, you perform your asynchronous operation or logic. It could be an API call, reading from a file, or any other asynchronous task.

If the operation succeeds, you call the resolve function and pass the result or value that should be resolved.

If an error occurs or the operation fails, you call the reject function and pass the error object or an explanation for the rejection.

Once the Promise is created, it can be used to handle the asynchronous result using .then() and .catch() methods

.then() Method: The .then() method is used to handle the successful fulfillment of a Promise. It allows you to specify a callback function that will be executed when the Promise is resolved (i.e., when it successfully completes its asynchronous operation).

The basic syntax for .then() is:

Here, onFulfilled is the callback function that will be invoked when the Promise is resolved and passed the resolved value as an argument. It is optional and can be omitted if you don't need to perform any specific action on resolution.

.catch() Method: The .catch() method is used to handle any errors or rejections that occur during the Promise chain. It allows you to specify a callback function that will be executed when the Promise is rejected.

The basic syntax for .catch() is:

Here, onRejected is the callback function that will be invoked when the Promise is rejected, and it receives the error or rejection reason as an argument.

By utilizing the .then() and .catch() methods, you can effectively handle the successful fulfillment and error handling of Promises, respectively. These methods allow you to chain asynchronous operations together and handle their outcomes in a structured and readable manner.

Chaining multiple asynchronous operations with Promises;

Chaining Promises opens a gateway to streamlined and sequential execution of tasks. Imagine a beautiful dance where each step gracefully follows the other, creating a mesmerizing performance. With Promises, you can orchestrate a sequence of asynchronous operations, one after the other, with elegance and ease. (The wider structure of this is covered better in the Advanced concepts section. This example is explained well also, no worries.)

Let's go through an example to illustrate Promise chaining:

In this example, we have a series of asynchronous tasks: fetching data from a server, processing the retrieved data, saving it, and displaying a success message. By chaining Promises together, we ensure that each task waits for the previous one to complete before proceeding to the next.

The first Promise, fetchData , fetches data from the provided url. Once the data is successfully retrieved, the then method is invoked, passing the data to the next Promise, processData . Inside processData , we can manipulate or transform the data as needed.

The chain continues with saveData , where we can save the processed data to a database or perform any other necessary actions. Finally, the last Promise in the chain, displaySuccessMessage , displays a message to inform the user about the successful completion of all the tasks.

If any Promise encounters an error, the chain bypasses the subsequent then callbacks and jumps directly to the catch method. The catch callback provides a graceful way to handle errors, ensuring that the flow of execution remains intact.

By chaining Promises, we achieve a smooth and organized flow of asynchronous operations. Each Promise plays its part in the grand performance, allowing us to break complex tasks into smaller, manageable pieces. This approach fosters code that is easier to understand, test, and maintain, and it paves the way for building responsive and efficient applications.

Error Handling in Promises:

Catching and handling errors using .catch();.

errors can occasionally disrupt the harmonious flow of our code. But fear not, for Promises come to our aid, offering elegant solutions to catch and handle these errors with poise and resilience. Imagine a tranquil garden where even the thorns are embraced and transformed into opportunities for growth.

When working with Promises, we can utilize the powerful .catch() method to intercept any errors that may occur during asynchronous operations. Let's embark on an example to unveil error handling with Promises:

In this enchanting chain of Promises, if any task encounters an error, the flow shifts to the .catch() method. This method acts as a guardian, catching the error and providing us with an opportunity to handle it.

Consider a scenario where the fetchData Promise encounters a network error while retrieving data from the provided url. The error would propagate down the chain, bypassing subsequent .then() callbacks and triggering the .catch() method. Inside the .catch() block, we can handle the error, whether it's displaying an error message to the user or logging it for further analysis.

The beauty of Promises lies in their ability to propagate errors up the chain. If the .catch() method is not defined at a particular level, the error continues its journey to the next higher-level .catch() block. This elegant propagation mechanism allows us to handle errors at appropriate levels, ensuring that our code remains resilient and responsive.

Introduction to async/await:

Overview of the async/await syntax as a modern approach to asynchronous programming;.

The async/await syntax is a modern and intuitive approach to asynchronous programming in JavaScript. It provides a more readable and sequential way of writing asynchronous code, making it easier to understand and maintain. With async/await, we can write asynchronous operations that look and feel like traditional synchronous code, unlocking a whole new level of simplicity and elegance.

The async/await syntax revolves around two keywords: async and await . By marking a function with the async keyword, we indicate that it contains asynchronous operations. Within this function, we can use the await keyword before a Promise, which pauses the execution of the function until the Promise is resolved. This allows us to write code that appears to be synchronous, without the need for callback functions or explicit Promise chaining. The use of try-catch blocks with async/await also simplifies error handling, allowing us to handle any potential errors that may occur during the asynchronous operations.

For example:

In this example, we have three async functions: fetchData , processData , and displayData . Each function performs a specific task in a sequential manner using the async/await syntax.

The fetchData function simulates fetching data asynchronously by introducing a delay of 2000 milliseconds using the delay helper function. The processData function awaits the completion of fetchData and processes the fetched data by converting it to uppercase. Finally, the displayData function awaits processData and logs the processed data.

If any error occurs during the asynchronous operations, the corresponding catch block is triggered, allowing us to handle the error and log an appropriate error message.

Converting Promises to async/await syntax:

Let's start with an example of a Promise;

In this Promise approach:

The fetchData function creates a Promise that resolves with the string "Hello, world!" after a delay of 2 seconds. The processData function takes the fetched data, converts it to uppercase, and returns a Promise that resolves with the processed data after a delay of 1 second. The displayData function calls fetchData , then chains the processData Promise, and finally logs the processed data to the console. Any errors occurring in either Promise are caught and logged using the .catch() method.

Let's then convert the above promise to async/await;

The fetchData function is marked as an async function. It uses the await keyword to pause execution for 2 seconds using the delay helper function and then returns the string "Hello, world!". The processData function is also an async function. It awaits the result of fetchData , converts it to uppercase after a 1-second delay, and returns the processed data. The displayData function is an async function that awaits fetchData and processData sequentially. It then logs the processed data to the console. Any errors occurring in the async functions are caught and logged using the try...catch block (discussed below).

Error handling with try/catch in async functions;

In the above async/await code, error handling is implemented using the try...catch block within async functions. Let's discuss how error handling is done:

In the displayData function, the code is wrapped inside a try block. The asynchronous operations, fetchData and processData , are awaited one after another. If any error occurs during the execution of either of these operations, the flow of control jumps to the catch block.

If an error is thrown within the try block or if a Promise is rejected, the execution immediately transfers to the catch block. The error object caught in the catch block is assigned to the error parameter, which can then be used to handle or log the error information.

In this case, if an error occurs during either fetchData or processData , the error will be caught in the catch block. The error message will be logged to the console using console.error("Error:", error).

Advanced Promise Concepts:

Promise.all(): executing multiple asynchronous operations in parallel;.

Promise.all() is a powerful feature in JavaScript that allows you to run multiple asynchronous operations concurrently and wait for all of them to complete. It takes an array of Promises as input and returns a new Promise that resolves when all the input Promises have resolved or rejects if any of the Promises are rejected. Here's an example to illustrate the above:

In this example, we have four asynchronous operations simulated by fetchData1() , fetchData2() , fetchData3() , and fetchData4() . Each function returns a Promise that resolves with some data after a certain delay, except for fetchData4() which deliberately rejects with an error.

We create an array fetchDataArray containing these Promises. By passing fetchDataArray to Promise.all() , we ensure that all Promises in the array are executed concurrently. The Promise.all() method returns a new Promise that resolves with an array of the resolved values if all Promises resolve successfully.

In the .then() block, we log a success message and the results if all the Promises resolve successfully. If any Promise in the array is rejected, the .catch() block is executed, logging the error.

By using Promise.all() , we can efficiently execute multiple asynchronous operations in parallel and handle their results collectively. This is especially useful when we need to wait for all the operations to complete before proceeding with further logic or when we want to handle errors collectively.

Promise.race(): Handling the first resolved or rejected Promise:

Promise.race() is a method in JavaScript that takes an array of Promises as input and returns a new Promise. This new Promise settles (resolves or rejects) as soon as the first Promise in the input array settles. Here's an illustration example:

In this example, we have three asynchronous operations simulated by fetchData1() , fetchData2() , and fetchData3() . Each function returns a Promise that resolves with some data after a certain delay.

We create an array fetchDataArray containing these Promises. By passing fetchDataArray to Promise.race() , we ensure that the resulting Promise settles as soon as the first Promise in the array settles, either by resolving or rejecting.

In the .then() block, we log a success message and the result of the first settled Promise. If any Promise in the array is rejected, the .catch() block is executed, logging the error.

By using Promise.race() , we can efficiently handle the first settled Promise among multiple asynchronous operations. This can be useful when we only care about the result of the fastest or earliest completed task, such as fetching data from different sources and responding with the first available data.

Promise chaining and composing asynchronous tasks:

Promise chaining allows you to execute a sequence of asynchronous operations in a more organized and readable way. It involves using the .then() method on a Promise to specify what should happen next when the Promise is resolved. Each .then() method returns a new Promise, which allows you to chain multiple asynchronous tasks together.

In this example, we have the fetchData() function that returns a Promise which resolves with some data after a 2000ms delay. We then chain multiple .then() methods to specify what should happen with that data at each step.

Inside the first .then() block, we log the received data and call processData() (assuming it's another asynchronous function) on that data. The returned Promise from processData() is automatically passed to the next .then() block.

Similarly, in the next .then() block, we log the processed result and call performAction() (assuming it's another asynchronous function) on the result. Again, the returned Promise is passed to the subsequent .then() block.

Finally, in the last `.then() block, we log the final result of the chained asynchronous tasks.

By using Promise chaining, you can compose and execute a series of asynchronous tasks in a structured manner, ensuring that each step depends on the successful completion of the previous step. If any Promise in the chain is rejected, the .catch() block will handle the error. This approach helps to avoid callback hell and leads to more maintainable and readable code.

Common Asynchronous Patterns and Best Practices:

Throttling and Debouncing Asynchronous Functions: Throttling and debouncing are techniques used to control the rate at which a function is invoked, especially in scenarios where the function can be called rapidly or frequently. Throttling limits the number of times a function can be executed within a specific time interval, while debouncing ensures that the function is only executed after a certain period of inactivity. These patterns are useful for handling events like scroll, resize, or input changes, where frequent updates may overwhelm the system. Throttling and debouncing help optimize performance and prevent unnecessary resource consumption.

Throttling example:

` // Throttling function to limit the frequency of function invocations function throttle(func, delay) { let timerId; // Reference to the timer

return function (...args) { if (!timerId) { // If no timer is running timerId = setTimeout(() => { func.apply(this, args); // Invoke the original function timerId = null; // Reset the timer }, delay); } }; }

// Throttle an event handler function to execute at most once every 200 milliseconds const throttledHandler = throttle((event) => { console.log("Throttled event:", event); }, 200);

// Attach the throttled event handler to an event element.addEventListener("scroll", throttledHandler);

` In this code, the throttle function is used to limit the frequency at which the event handler function is executed. The throttling is achieved by setting a timer that waits for the specified delay period before invoking the original function. If subsequent events occur within the delay period, the timer is reset, effectively throttling the function.

The throttle function returns a new function that acts as the throttled event handler. When this throttled handler is attached to an event, it ensures that the original event handler function is invoked at most once every delay milliseconds. This helps to control the frequency of function invocations and optimize performance in scenarios where rapid event triggering can occur, such as scroll events.

Debouncing example:

` // Debouncing function to delay the execution of a function until a period of inactivity function debounce(func, delay) { let timerId; // Reference to the timer

return function (...args) { clearTimeout(timerId); // Clear the previous timer

// Debounce an input handler function to execute after 500 milliseconds of inactivity const debouncedHandler = debounce((value) => { console.log("Debounced value:", value); }, 500);

// Attach the debounced event handler to an input element inputElement.addEventListener("input", (event) => { debouncedHandler(event.target.value); // Pass the input value to the debounced handler });

` In this code, the debounce function is used to delay the execution of a function until a period of inactivity has occurred. When the debounced function is invoked, it sets a timer to wait for the specified delay period. If subsequent invocations happen within the delay period, the previous timer is cleared and a new one is set, effectively debouncing the function.

The debounce function returns a new function that acts as the debounced event handler. This handler is attached to an input element's "input" event. Whenever the user types in the input, the debounced handler is called with the current value. If there is no further input activity for the specified delay, the original function is invoked with the last input value. This helps to optimize performance and control the frequency of function execution, especially in scenarios where rapid input changes may occur, such as autocomplete or live search functionalities.

Handling Concurrent Asynchronous Tasks: In many cases, there is a need to execute multiple asynchronous tasks concurrently. This can be achieved using techniques such as Promise.all() , which allows you to wait for multiple Promises to resolve before proceeding. By executing tasks in parallel, you can improve the overall efficiency and speed of your application. However, it's important to consider resource limitations and avoid excessive concurrent operations that may impact performance. Check out the example on the Promise.all()

Caching and Memoization of Async Results: Caching and memoization involve storing the results of expensive or time-consuming asynchronous operations to avoid unnecessary recomputation or network requests. By caching the results, subsequent requests for the same data can be served from the cache, reducing latency and improving responsiveness. Memoization takes caching a step further by associating specific input arguments with their respective results. This technique is particularly useful when the same inputs are likely to be requested multiple times. However, care should be taken to manage cache expiration and invalidation to ensure data integrity.

// Function to fetch data from an API or retrieve from cache if available function fetchData(id) { if (cache.has(id)) { // Check if the data is already cached return Promise.resolve(cache.get(id)); // Resolve with the cached data } else { return fetch( /api/data/${id}`) // Fetch the data from the API .then((response) => response.json()) // Extract the JSON response .then((data) => { cache.set(id, data); // Cache the fetched data for future use return data; // Resolve with the fetched data }); } }

// Usage example: Fetch data with ID 1 fetchData(1) .then((data) => { console.log("Data 1:", data); }) .catch((error) => { console.error("An error occurred:", error); });

// Subsequent call to the same data will be served from the cache fetchData(1) .then((data) => { console.log("Cached Data 1:", data); }) .catch((error) => { console.error("An error occurred:", error); });

` In this code, the fetchData function is responsible for fetching data from an API, with an added caching mechanism to avoid unnecessary API calls for the same data.

When fetchData is called, it first checks if the requested data is available in the cache. If so, it resolves the promise immediately with the cached data using Promise.resolve() . If the data is not in the cache, it makes a request to the API using the fetch function. Once the response is received, it is parsed as JSON using response.json() and then stored in the cache using the cache.set() method. Finally, the function resolves the promise with the fetched data.

In the usage example, fetchData(1) is called twice. The first call fetches the data from the API and logs it to the console. The subsequent call to the same data (ID 1) is served directly from the cache without making another API request. This caching mechanism helps optimize performance by reducing unnecessary network requests and serving data faster from the cache.

Avoiding Callback Hell and Writing Clean Asynchronous Code: Callback hell refers to the nested and convoluted structure of code that arises when dealing with multiple asynchronous operations using callbacks. To avoid this, modern JavaScript provides several mechanisms, such as Promises and async/await, that simplify asynchronous code and make it more readable and maintainable. Promises allow for more linear code flow and error handling through the use of .then() and .catch() methods. The async/await syntax takes it a step further by providing a more synchronous-like programming style while still working with asynchronous operations. These approaches promote cleaner code organization and easier error handling, leading to more efficient development and maintenance.

Asynchronous JavaScript in Different Environments:

Asynchronous JavaScript is a fundamental aspect of modern web development, and it plays a crucial role in different environments, such as the browser and Node.js. Understanding how asynchronous programming works in these environments is essential for building efficient and responsive applications.

In the browser environment, asynchronous programming is commonly used for tasks like making AJAX requests and fetching data from APIs. Traditionally, the XMLHttpRequest object was used for AJAX, but nowadays, the Fetch API (click that link to read more about Fetch API) provides a more modern and flexible way to handle asynchronous operations. With Fetch, you can send HTTP requests and receive responses asynchronously, making it easier to update web pages dynamically without blocking the main thread. By utilizing techniques like promises or async/await, you can handle the asynchronous flow more effectively, improving the user experience.

In Node.js, asynchronous programming is crucial for handling tasks like file system operations and network requests. Node.js is designed to be non-blocking and event-driven, allowing multiple operations to be executed concurrently. This is particularly important for scenarios where the server needs to handle many simultaneous connections efficiently. Node.js provides built-in modules, such as fs for file system operations and http for creating HTTP servers, that support asynchronous operations. By leveraging callbacks, promises, or async/await syntax, you can perform tasks asynchronously, improving the responsiveness and scalability of your Node.js applications.

Asynchronous Libraries and Frameworks:

Axios: Axios is a widely used library for making HTTP requests in both the browser and Node.js. It provides a simple and intuitive API for performing asynchronous operations, such as fetching data from APIs. Axios supports promises by default and also allows you to leverage async/await syntax for cleaner and more readable code. With features like request and response interceptors, automatic JSON parsing, and error handling, Axios simplifies the process of handling asynchronous operations and working with remote data.

Async.js: Async.js is a powerful utility library for handling asynchronous tasks in Node.js and the browser. It provides a wide range of functions that help manage asynchronous control flow, such as parallel execution, sequential execution, and error handling. Async.js offers methods like async.parallel, async.series, and async.waterfall, which allow you to execute multiple asynchronous tasks and handle the results in a controlled and organized manner. It also supports callback-style programming and can transform callback-based functions into promise-based functions.

Bluebird: Bluebird is a feature-rich promise library that provides advanced features like cancellation, timeouts, and concurrency control. It offers a powerful API for working with promises and enhancing their capabilities.

RxJS: RxJS is a reactive programming library that introduces the concept of observables. It allows you to work with asynchronous streams of data and provides powerful operators for transforming, filtering, and combining these streams.

Redux-Thunk: Redux-Thunk is a middleware for Redux, a popular state management library. It enables you to write asynchronous logic in your Redux applications by dispatching thunk functions, which are functions that can return promises or perform async operations.

Q: Q is a widely used promise library that provides a robust and feature-rich API. It supports both callback-style and promise-style APIs and offers additional functionality such as deferreds and progress notifications.

Async/await with ES6: Asynchronous programming can also be simplified using native JavaScript features like async/await. With async functions and the await keyword, you can write asynchronous code that looks similar to synchronous code, improving readability and maintainability.

Performance and Optimization

1. Use asynchronous operations wisely: Asynchronous operations can greatly improve performance by allowing non-blocking execution. However, it's important to use them judiciously and avoid unnecessary or excessive asynchronous calls. Minimize the number of requests or operations by grouping them together when possible.

2. Threading and worker pools: In environments that support multithreading, such as Node.js, you can leverage threading and worker pools to handle concurrent tasks effectively. By distributing the workload across multiple threads or workers, you can take advantage of parallel processing and optimize the performance of CPU-intensive or I/O-bound operations.

3. Implement proper error handling: Efficient error handling is crucial for maintaining the stability and performance of asynchronous code. Unhandled errors or inefficient error handling can lead to crashes or delays. Make sure to catch and handle errors appropriately, and consider implementing mechanisms like retrying failed operations or implementing fallback strategies when encountering errors.

4. Profile and optimize: Profiling tools can help you identify performance bottlenecks in your asynchronous code. Use tools like Chrome DevTools or Node.js profilers to analyze the execution time and identify areas that can be optimized. Consider optimizing resource-intensive operations, reducing unnecessary computations, or improving algorithm efficiency.

5. Debugging asynchronous code: Debugging asynchronous code can be challenging due to its non-linear flow. Utilize debugging tools and techniques specific to asynchronous programming, such as setting breakpoints, stepping through asynchronous code, or using tools like async/await breakpoints. These tools can help you trace the execution flow, analyze variable states, and diagnose issues effectively.

Tips for Debugging Asynchronous Code:

Strategies and tools for debugging asynchronous javascript;.

1. Use console.log and debugging statements: Inserting console.log statements at key points in your code can provide insights into the flow of execution and help you track the values of variables. Use console.log to output relevant information and trace the program flow during asynchronous operations.

2. Leverage browser developer tools: Modern browsers come with powerful developer tools that offer debugging capabilities for asynchronous JavaScript. Utilize the console to log messages and inspect variable values. Set breakpoints in your code to pause execution and examine the program's state. Step through asynchronous code using tools like async/await breakpoints or the "step into" feature.

3. Employ debugging libraries and frameworks: There are libraries and frameworks available specifically designed for debugging asynchronous JavaScript code. For example, the "async-tracker" library allows you to trace and visualize the execution flow of asynchronous operations, helping you identify potential issues. Similarly, frameworks like React and Vue.js provide debugging tools tailored to their asynchronous rendering models.

4. Use error handling mechanisms: Proper error handling is crucial when debugging asynchronous code. Make sure to handle errors effectively and log them with meaningful messages. Utilize try/catch blocks, .catch() handlers on promises, or error handling mechanisms provided by libraries or frameworks. This allows you to capture and handle errors gracefully, making debugging more manageable.

5. Trace asynchronous operations: Consider using tools that enable you to trace the flow of asynchronous operations. These tools can help you visualize the sequence of asynchronous function calls, identify bottlenecks, and understand the order of execution. Tools like "async_hooks" in Node.js provide low-level hooks for tracking asynchronous operations.

6. Test in controlled environments: When debugging asynchronous code, it can be helpful to recreate the issue in a controlled environment. This might involve writing specific test cases or utilizing tools like mock APIs or simulated network delays. By isolating the problematic code and testing it in a controlled setting, you can focus your debugging efforts and avoid unnecessary complexities.

7. Analyze error messages and stack traces: When errors occur in asynchronous code, carefully examine the error messages and stack traces. They often provide valuable information about the source of the error, including file names and line numbers. Analyzing error messages and stack traces can help you identify the root cause of the issue and guide your debugging process.

8. Seek help from asynchronous programming resources: Asynchronous programming can be complex, and there are dedicated resources available to help you understand and debug it. Online forums, documentation, and tutorials specific to the libraries or frameworks you are using can provide valuable insights and solutions to common asynchronous debugging challenges.

Common pitfalls and how to avoid them;

1. Unhandled errors: Failing to handle errors in asynchronous operations can lead to unexpected crashes or undefined behavior. Always include proper error handling mechanisms such as try/catch blocks, .catch() handlers on promises, or error callbacks. Handle errors gracefully by logging meaningful error messages and taking appropriate actions.

2. Callback hell: Callback hell occurs when you have multiple nested callbacks, making the code difficult to read and maintain. To avoid this, consider using promises or async/await syntax, which provide a more linear and readable way to handle asynchronous operations. Promises allow you to chain asynchronous tasks, while async/await simplifies the syntax by using asynchronous functions with a synchronous-like flow.

3. Incorrect order of execution: Asynchronous operations don't always execute in the order they appear in the code. This can lead to race conditions or unexpected behavior. To ensure the desired order of execution, use techniques like Promise.all() or async/await to synchronize multiple asynchronous tasks. Properly chaining promises or using async/await can help maintain the expected sequence of operations.

4. Overusing synchronous patterns: Asynchronous code should be leveraged when dealing with long-running tasks or I/O operations. Avoid unnecessarily converting asynchronous operations into synchronous ones, as it can block the event loop and impact the performance of your application. Be mindful of using synchronous patterns, such as synchronous AJAX requests, when they are not necessary.

5. Inefficient resource usage: Asynchronous operations can consume system resources, such as memory or network connections. It's important to manage resources efficiently to prevent bottlenecks and improve performance. For example, avoid making excessive network requests or creating too many concurrent tasks. Consider techniques like throttling or debouncing to control the frequency of asynchronous operations.

6. Lack of testing and debugging: Asynchronous code can be challenging to test and debug due to its non-linear nature. Neglecting thorough testing and debugging can lead to hard-to-find bugs. Invest time in writing unit tests that cover different scenarios of your asynchronous code. Utilize debugging techniques and tools specific to asynchronous code, as mentioned earlier, to identify and fix issues.

7. Ignoring callback or promise errors: It's important to handle errors in callbacks or promises properly. Ignoring errors can lead to silent failures or undesired behavior. Always check for errors in callbacks or use .catch() handlers on promises to handle potential errors. Logging or reporting errors can help diagnose issues and improve the reliability of your code.

That's a wrap, I hope that helped. Don't forget to like, comment, share and follow.

Top comments (0)

pic

Templates let you quickly answer FAQs or store snippets for re-use.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink .

Hide child comments as well

For further actions, you may consider blocking this person and/or reporting abuse

akash32755 profile image

Setting Up Toast Custom Notifications in React with Specific Types and Context API

Akash Yadav - Jul 23

barirah profile image

My first single file webpage I have no idea if this is right so pls give me some feedback

Barirah - Jul 24

virtualsobriety profile image

JavaScript30 - 10 Hold Shift to Check Multiple Checkboxes!

VirtualSobriety - Jul 22

doozieakshay profile image

Handling and debugging CORS (Cross-Origin Resource Sharing) issues in a NestJS Application

Akshay Joshi - Jul 27

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Advisory boards aren’t only for executives. Join the LogRocket Content Advisory Board today →

LogRocket blog logo

  • Product Management
  • Solve User-Reported Issues
  • Find Issues Faster
  • Optimize Conversion and Adoption
  • Start Monitoring for Free

A guide to async/await in TypeScript

await job assignment

Editor’s note : This article was last updated by Oyinkansola Awosan on 15 February 2024 to include information about error handling with try/catch blocks and higher-order functions, as well as to feature TypeScript’s Awaited type.

A Guide To Async/Await In TypeScript

If you’re reading this blog, you probably have some familiarity with asynchronous programming in JavaScript, and you may be wondering how it works in TypeScript.

Asynchronous programming is a way of writing code that can carry out tasks independently of each other, not needing one task to be completed before another gets started. When you think of asynchronous programming, think of multitasking and effective time management.

Introduction to async/await in TypeScript

TypeScript is a superset of JavaScript, so async/await works the same, but with some extra goodies and type safety. TypeScript enables you to ensure type safety for the expected result and even check for type errors, which helps you detect bugs earlier in the development process.

async/await is essentially a syntactic sugar for promises, which is to say that the async/await keyword is a wrapper over promises. An async function always returns a promise. Even if you omit the Promise keyword, the compiler will wrap your function in an immediately resolved promise.

Here’s an example:

Although they look different, the code snippets above are more or less equivalent.

async/await simply enables you to write the code more synchronously and unwraps the promise within the same line of code for you. This is powerful when you’re dealing with complex asynchronous patterns.

To get the most out of the async/await syntax, you’ll need a basic understanding of promises.

What is a promise in TypeScript?

In JavaScript, a “promise” refers to the expectation that something will happen at a particular time, enabling your app to use the result of that future event to perform certain other tasks.

To demonstrate what I mean, I’ll break down a real-world example and translate it into pseudocode, followed by the actual TypeScript code.

Let’s say I have a lawn to mow. I contact a mowing company that promises to mow my lawn in a couple of hours. In turn, I promise to pay them immediately afterward, provided the lawn is properly mowed.

Can you spot the pattern? The first obvious thing to note is that the second event relies entirely on the previous one. If the first event’s promise is fulfilled, the next event’s will be executed. The promise in that event is then either fulfilled, rejected, or remains pending.

Over 200k developers use LogRocket to create better digital experiences

await job assignment

Let’s look at this sequence step by step and then explore its code:

Diagram Showing a Promise Sequence in TypeScript

The promise syntax

Before we write out the full code, it makes sense to examine the syntax for a promise — specifically, an example of a promise that resolves into a string.

We declared a promise with the new + Promise keyword, which takes in the resolve and reject arguments. Now let’s write a promise for the flow chart above:

In the code above, we declared both the company’s promises and our promises. The company promise is either resolved after 100,000ms or rejected. A Promise is always in one of three states: resolved if there is no error, rejected if an error is encountered, or pending if the promise has been neither rejected nor fulfilled. In our case, it falls within the 100000ms period.

But how can we execute the task sequentially and synchronously? That’s where the then keyword comes in. Without it, the functions simply run in the order in which they resolve.

Sequential execution with .then

Now we can chain the promises, which allows them to run in sequence with .then . This functions like a normal human language — do this and then that and then that, and so on.

The code below will run the angelMowersPromise . If there is no error, it’ll run the myPaymentPromise . If there is an error in either of the two promises, it’ll be caught in the catch block:

Now let’s look at a more technical example. A common task in frontend programming is to make network requests and respond to the results accordingly.

Below is a request to fetch a list of employees from a remote server:

There may be times when you need numerous promises to execute in parallel or sequence. Constructs such as Promise.all or Promise.race are especially helpful in these scenarios.

For example, imagine that you need to fetch a list of 1,000 GitHub users, and then make an additional request with the ID to fetch avatars for each of them. You don’t necessarily want to wait for each user in the sequence; you just need all the fetched avatars. We’ll examine this in more detail later when we discuss Promise.all .

Now that you have a fundamental grasp of promises, let’s look at the async/await syntax.

Understanding async/await

The async/await syntax simplifies working with promises in JavaScript. It provides an easy interface to read and write promises in a way that makes them appear synchronous.

An async/await will always return a Promise . Even if you omit the Promise keyword, the compiler will wrap the function in an immediately resolved Promise . This enables you to treat the return value of an async function as a Promise , which is useful when you need to resolve numerous asynchronous functions.

As the name implies, async always goes hand in hand with await . That is, you can only await inside an async function. The async function informs the compiler that this is an asynchronous function.

If we convert the promises from above, the syntax looks like this:

As you can immediately see, this looks more readable and appears synchronous. We told the compiler on line 3 to await the execution of angelMowersPromise before doing anything else. Then, we return the response from the myPaymentPromise .

You may have noticed that we omitted error handling. We could do this with the catch block after the .then in a promise. But what happens if we encounter an error? That leads us to try/catch .

Error handling with try/catch

We’ll refer to the employee fetching example to see the error handling in action, as it is likely to encounter an error over a network request.

Let’s say, for example, that the server is down, or perhaps we sent a malformed request. We need to pause execution to prevent our program from crashing. The syntax will look like this:

We initiated the function as an async function. We expect the return value to be either an array of employees or a string of error messages. Therefore, the type of promise is Promise<Array<Employee> | string> .

Inside the try block are the expressions we expect the function to run if there are no errors. Meanwhile, the catch block captures any errors that arise. In that case, we’d just return the message property of the error object.

The beauty of this is that any error that first occurs within the try block is thrown and caught in the catch block. An uncaught exception can lead to hard-to-debug code or even break the entire program.

Error handling using higher-order functions

While traditional try/catch blocks are effective for catching errors at the local level, they can become repetitive and clutter the main business logic when used too frequently. This is where higher-order functions come into play.

A higher-order function is a function that takes one or more functions as arguments or returns a function. In the context of error handling, a higher-order function can wrap an asynchronous function (async function) and handle any errors it might throw, thereby abstracting the try/catch logic away from the core business logic.

The main idea behind using higher-order functions for error handling in async/await is to create a wrapper function that takes an async function as an argument along with any parameters that the async function might need. Inside this wrapper, we implement a try/catch block. This approach allows us to handle errors in a centralized manner, making the code cleaner and more maintainable.

Let’s refer to the employee fetching example:

In this example, the safeFetchEmployees function uses the handleAsyncErrors higher-order function to wrap the original fetchEmployees function.

This setup automatically handles any errors that might occur during the API call, logging them and returning null to indicate an error state. The consumer of safeFetchEmployees can then check if the returned value is null to determine if the operation was successful or if an error occurred.

Concurrent execution with Promise.all

As mentioned earlier, there are times when we need promises to execute in parallel.

Let’s look at an example from our employee API. Say we first need to fetch all employees, then fetch their names, and then generate an email from the names. Obviously, we’ll need to execute the functions in a synchronous manner and also in parallel so that one doesn’t block the other.

In this case, we would make use of Promise.all . According to Mozilla , “ Promise.all is typically used after having started multiple asynchronous tasks to run concurrently and having created promises for their results so that one can wait for all the tasks being finished.”

In pseudocode, we’d have something like this:

  • Fetch all users => /employee
  • Wait for all user data. Extract the id from each user. Fetch each user => /employee/{id}
  • Generate an email for each user from their username

In the above code, fetchEmployees fetches all the employees from the baseApi . We await the response, convert it to JSON , and then return the converted data.

The most important concept to keep in mind is how we sequentially executed the code line by line inside the async function with the await keyword. We’d get an error if we tried to convert data to JSON that has not been fully awaited. The same concept applies to fetchEmployee , except that we’d only fetch a single employee. The more interesting part is the runAsyncFunctions , where we run all the async functions concurrently.

First, wrap all the methods within runAsyncFunctions inside a try/catch block. Next, await the result of fetching all the employees. We need the id of each employee to fetch their respective data, but what we ultimately need is information about the employees.

This is where we can call upon Promise.all to handle all the Promises concurrently. Each fetchEmployee Promise is executed concurrently for all the employees. The awaited data from the employees’ information is then used to generate an email for each employee with the generateEmail function.

In the case of an error, it propagates as usual, from the failed promise to Promise.all , and then becomes an exception we can catch inside the catch block.

The Awaited type

Awaited is a utility type that models operations like await in async functions. It unwraps the resolved value of a promise, discarding the promise itself, and works recursively, thereby removing any nested promise layers as well.

Awaited is the type of value that you expect to get after awaiting a promise. It helps your code understand that once you use await , you’re not dealing with a promise anymore, but with the actual data you wanted.

Here’s the basic syntax:

The awaited type does not exactly model the .then method in promises, however, Awaited can be relevant when using .then in async functions. If you use await inside a .then callback, Awaited helps infer the type of the awaited value, avoiding the need for additional type annotations.

Awaited can help clarify the type of data and awaitedValue in async functions, even when using .then for promise chaining. However, it doesn’t replace the functionality of .then itself.

Key takeaways

async and await enable us to write asynchronous code in a way that looks and behaves like synchronous code. This makes the code much easier to read, write, and understand.

Here are some some key concepts to keep in mind as you’re working on your next asynchronous project in TypeScript:

LogRocket : Full visibility into your web and mobile apps

LogRocket Dashboard Free Trial Banner

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.

Try it for free .

  • await only works inside an async function
  • The function marked with the async keyword always returns a Promise
  • If the return value inside async doesn’t return a Promise , it will be wrapped in an immediately resolved Promise
  • Execution is paused when an await keyword is encountered until a Promise is completed
  • await will either return a result from a fulfilled Promise or throw an exception from a rejected Promise

Share this:

  • Click to share on Twitter (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • #typescript

Hey there, want to help make our blog better?

Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

await job assignment

Stop guessing about your digital experience with LogRocket

Recent posts:.

await job assignment

Building a LeetCode-style code evaluator with isolated-vm

Running untrusted code in a JavaScript environment like Node.js has always posed serious risks. Node has access to the network […]

await job assignment

ESLint adoption guide: Overview, examples, and alternatives

ESLint is well adopted and covers virtually all relevant development use cases with its rich feature set. Learn more in this guide.

await job assignment

JavaScript design patterns guide

Imagine a situation where a group of architects wants to design a skyscraper. During the design stage, they would have […]

await job assignment

Using qr-code: a customizable, animate-able HTML element

QR code generation algorithms can turn textual data such as URLs, emails, phone numbers, and Wi-Fi connection details, into dynamically […]

await job assignment

2 Replies to "A guide to async/await in TypeScript"

Logrocket does not catch uncaught promise rejections (at least in our case).

It can catch uncaught promise rejections—it just doesn’t catch them automatically. You can manually set it up to do so!

Leave a Reply Cancel reply

Async JavaScript: From Callbacks, to Promises, to Async/Await

One of my favorite sites is BerkshireHathaway.com - it's simple, effective, and has been doing its job well since it launched in 1997. Even more remarkable, over the last 20 years, there's a good chance this site has never had a bug. Why? Because it's all static. It's been pretty much the same since it launched over 20 years ago. Turns out sites are pretty simple to build if you have all of your data up front. Unfortunately, most sites now days don't. To compensate for this, we've invented "patterns" for handling fetching external data for our apps. Like most things, these patterns each have tradeoffs that have changed over time. In this post, we'll break down the pros and cons of three of the most common patterns, Callbacks , Promises , and Async/Await and talk about their significance and progression from a historical context.

Let's start with the OG of these data fetching patterns, Callbacks.

I'm going to assume you know exactly 0 about callbacks. If I'm assuming wrong, just scroll down a bit.

When I was first learning to program, it helped me to think about functions as machines. These machines can do anything you want them to. They can even accept input and return a value. Each machine has a button on it that you can press when you want the machine to run, ().

Whether I press the button, you press the button, or someone else presses the button doesn't matter. Whenever the button is pressed, like it or not, the machine is going to run.

In the code above we assign the add function to three different variables, me , you , and someoneElse . It's important to note that the original add and each of the variables we created are pointing to the same spot in memory. They're literally the exact same thing under different names. So when we invoke me , you , or someoneElse , it's as if we're invoking add .

Now, what if we take our add machine and pass it to another machine? Remember, it doesn't matter who presses the () button, if it's pressed, it's going to run.

Your brain might have got a little weird on this one, nothing new is going on here though. Instead of "pressing the button" on add , we pass add as an argument to addFive , rename it addReference , and then we "press the button" or invoke it.

This highlights some important concepts of the JavaScript language. First, just as you can pass a string or a number as an argument to a function, so too can you pass a reference to a function as an argument. When you do this the function you're passing as an argument is called a callback function and the function you're passing the callback function to is called a higher order function .

Because vocabulary is important, here's the same code with the variables re-named to match the concepts they're demonstrating.

This pattern should look familiar, it's everywhere. If you've ever used any of the JavaScript Array methods, you've used a callback. If you've ever used lodash, you've used a callback. If you've ever used jQuery, you've used a callback.

In general, there are two popular use cases for callbacks. The first, and what we see in the .map and _.filter examples, is a nice abstraction over transforming one value into another. We say "Hey, here's an array and a function. Go ahead and get me a new value based on the function I gave you". The second, and what we see in the jQuery example, is delaying execution of a function until a particular time. "Hey, here's this function. Go ahead and invoke it whenever the element with an id of btn is clicked." It's this second use case that we're going to focus on, "delaying execution of a function until a particular time".

Right now we've only looked at examples that are synchronous. As we talked about at the beginning of this post, most of the apps we build don't have all the data they need up front. Instead, they need to fetch external data as the user interacts with the app. We've just seen how callbacks can be a great use case for this because, again, they allow you to "delay execution of a function until a particular time". It doesn't take much imagination to see how we can adapt that sentence to work with data fetching. Instead of delaying execution of a function until a particular time , we can delay execution of a function until we have the data we need . Here's probably the most popular example of this, jQuery's getJSON method.

We can't update the UI of our app until we have the user's data. So what do we do? We say, "Hey, here's an object. If the request succeeds, go ahead and call success passing it the user's data. If it doesn't, go ahead and call error passing it the error object. You don't need to worry about what each method does, just be sure to call them when you're supposed to". This is a perfect demonstration of using a callback for async requests.

At this point, we've learned about what callbacks are and how they can be beneficial both in synchronous and asynchronous code. What we haven't talked yet is the dark side of callbacks. Take a look at this code below. Can you tell what's happening?

If it helps, you can play around with the live version here .

Notice we've added a few more layers of callbacks. First, we're saying don't run the initial AJAX request until the element with an id of btn is clicked. Once the button is clicked, we make the first request. If that request succeeds, we make a second request. If that request succeeds, we invoke the updateUI method passing it the data we got from both requests. Regardless of if you understood the code at first glance or not, objectively it's much harder to read than the code before. This brings us to the topic of "Callback Hell".

As humans, we naturally think sequentially. When you have nested callbacks inside of nested callbacks, it forces you out of your natural way of thinking. Bugs happen when there's a disconnect between how your software is read and how you naturally think.

Like most solutions to software problems, a commonly prescribed approach for making "Callback Hell" easier to consume is to modularize your code.

OK, the function names help us understand what's going on, but is it objectively "better"? Not by much. We've put a band-aid over the readability issue of Callback Hell. The problem still exists that we naturally think sequentially and, even with the extra functions, nested callbacks break us out of that sequential way of thinking.

The next issue of callbacks has to do with inversion of control . When you write a callback, you're assuming that the program you're giving the callback to is responsible and will call it when (and only when) it's supposed to. You're essentially inverting the control of your program over to another program. When you're dealing with libraries like jQuery, lodash, or even vanilla JavaScript, it's safe to assume that the callback function will be invoked at the correct time with the correct arguments. However, for many third-party libraries, callback functions are the interface for how you interact with them. It's entirely plausible that a third party library could, whether on purpose or accidentally, break how they interact with your callback.

Since you're not the one calling criticalFunction , you have 0 control over when and with what argument it's invoked. Most of the time this isn't an issue, but when it is, it's a big one.

Have you ever been to a busy restaurant without a reservation? When this happens, the restaurant needs a way to get back in contact with you when a table opens up. Historically, they'd just take your name and yell it when your table was ready. Then, as naturally occurs, they decided to start getting fancy. One solution was, instead of taking your name, they'd take your number and text you once a table opened up. This allowed you to be out of yelling range but more importantly, it allowed them to target your phone with ads whenever they wanted. Sound familiar? It should! OK, maybe it shouldn't. It's a metaphor for callbacks! Giving your number to a restaurant is just like giving a callback function to a third party service. You expect the restaurant to text you when a table opens up, just like you expect the third party service to invoke your function when and how they said they would. Once your number or callback function is in their hands though, you've lost all control.

Thankfully, there is another solution that exists. One that, by design, allows you to keep all the control. You've probably even experienced it before - it's that little buzzer thing they give you. You know, this one.

Restaurant Buzzer

If you've never used one before, the idea is simple. Instead of taking your name or number, they give you this device. When the device starts buzzing and glowing, your table is ready. You can still do whatever you'd like as you're waiting for your table to open up, but now you don't have to give up anything. In fact, it's the exact opposite. They have to give you something. There is no inversion of control.

The buzzer will always be in one of three different states - pending , fulfilled , or rejected .

pending is the default, initial state. When they give you the buzzer, it's in this state.

fulfilled is the state the buzzer is in when it's flashing and your table is ready.

rejected is the state the buzzer is in when something goes wrong. Maybe the restaurant is about to close or they forgot someone rented out the restaurant for the night.

Again, the important thing to remember is that you, the receiver of the buzzer, have all the control. If the buzzer gets put into fulfilled , you can go to your table. If it gets put into fulfilled and you want to ignore it, cool, you can do that too. If it gets put into rejected , that sucks but you can go somewhere else to eat. If nothing ever happens and it stays in pending , you never get to eat but you're not actually out anything.

Now that you're a master of the restaurant buzzer thingy, let's apply that knowledge to something that matters.

If giving the restaurant your number is like giving them a callback function, receiving the little buzzy thing is like receiving what's called a "Promise".

As always, let's start with why . Why do Promises exist? They exist to make the complexity of making asynchronous requests more manageable. Exactly like the buzzer, a Promise can be in one of three states, pending , fulfilled or rejected . Unlike the buzzer, instead of these states representing the status of a table at a restaurant, they represent the status of an asynchronous request.

If the async request is still ongoing, the Promise will have a status of pending . If the async request was successfully completed, the Promise will change to a status of fulfilled . If the async request failed, the Promise will change to a status of rejected . The buzzer metaphor is pretty spot on, right?

Now that you understand why Promises exist and the different states they can be in, there are three more questions we need to answer.

  • How do you create a Promise?
  • How do you change the status of a promise?
  • How do you listen for when the status of a promise changes?

1) How do you create a Promise?

This one is pretty straight forward. You create a new instance of Promise .

2) How do you change the status of a promise?

The Promise constructor function takes in a single argument, a (callback) function. This function is going to be passed two arguments, resolve and reject .

resolve - a function that allows you to change the status of the promise to fulfilled

reject - a function that allows you to change the status of the promise to rejected .

In the code below, we use setTimeout to wait 2 seconds and then invoke resolve . This will change the status of the promise to fulfilled .

We can see this change in action by logging the promise right after we create it and then again roughly 2 seconds later after resolve has been called.

Notice the promise goes from <pending> to <resolved> .

3) How do you listen for when the status of a promise changes?

In my opinion, this is the most important question. It's cool we know how to create a promise and change its status, but that's worthless if we don't know how to do anything after the status changes.

One thing we haven't talked about yet is what a promise actually is. When you create a new Promise , you're really just creating a plain old JavaScript object. This object can invoke two methods, then , and catch . Here's the key. When the status of the promise changes to fulfilled , the function that was passed to .then will get invoked. When the status of a promise changes to rejected , the function that was passed to .catch will be invoked. What this means is that once you create a promise, you'll pass the function you want to run if the async request is successful to .then . You'll pass the function you want to run if the async request fails to .catch .

Let's take a look at an example. We'll use setTimeout again to change the status of the promise to fulfilled after two seconds (2000 milliseconds).

If you run the code above you'll notice that roughly 2 seconds later, you'll see "Success!" in the console. Again the reason this happens is because of two things. First, when we created the promise, we invoked resolve after ~2000 milliseconds - this changed the status of the promise to fulfilled . Second, we passed the onSuccess function to the promises' .then method. By doing that we told the promise to invoke onSuccess when the status of the promise changed to fulfilled which it did after ~2000 milliseconds.

Now let's pretend something bad happened and we wanted to change the status of the promise to rejected . Instead of calling resolve , we would call reject .

Now this time instead of the onSuccess function being invoked, the onError function will be invoked since we called reject .

Now that you know your way around the Promise API, let's start looking at some real code.

Remember the last async callback example we saw earlier?

Is there any way we could use the Promise API here instead of using callbacks? What if we wrap our AJAX requests inside of a promise? Then we can simply resolve or reject depending on how the request goes. Let's start with getUser .

Nice. Notice that the parameters of getUser have changed. Instead of receiving id , onSuccess , and onFailure , it just receives id . There's no more need for those other two callback functions because we're no longer inverting control. Instead, we use the Promise's resolve and reject functions. resolve will be invoked if the request was successful, reject will be invoked if there was an error.

Next, let's refactor getWeather . We'll follow the same strategy here. Instead of taking in onSuccess and onFailure callback functions, we'll use resolve and reject .

Looking good. Now the last thing we need to update is our click handler. Remember, here's the flow we want to take.

  • Get the user's information from the Github API.
  • Use the user's location to get their weather from the Yahoo Weather API.
  • Update the UI with the user's info and their weather.

Let's start with #1 - getting the user's information from the Github API.

Notice that now instead of getUser taking in two callback functions, it returns us a promise that we can call .then and .catch on. If .then is called, it'll be called with the user's information. If .catch is called, it'll be called with the error.

Next, let's do #2 - Use the user's location to get their weather.

Notice we follow the exact same pattern we did in #1 but now we invoke getWeather passing it the user object we got from userPromise .

Finally, #3 - Update the UI with the user's info and their weather.

Here's the full code you can play around with.

Our new code is better , but there are still some improvements we can make. Before we can make those improvements though, there are two more features of promises you need to be aware of, chaining and passing arguments from resolve to then .

Both .then and .catch will return a new promise. That seems like a small detail but it's important because it means that promises can be chained.

In the example below, we call getPromise which returns us a promise that will resolve in at least 2000 milliseconds. From there, because .then will return a promise, we can continue to chain our .then s together until we throw a new Error which is caught by the .catch method.

Cool, but why is this so important? Remember back in the callback section we talked about one of the downfalls of callbacks being that they force you out of your natural, sequential way of thinking. When you chain promises together, it doesn't force you out of that natural way of thinking because chained promises are sequential. getPromise runs then logA runs then logB runs then... .

Just so you can see one more example, here's a common use case when you use the fetch API. fetch will return you a promise that will resolve with the HTTP response. To get the actual JSON, you'll need to call .json . Because of chaining, we can think about this in a sequential manner.

Now that we know about chaining, let's refactor our getUser / getWeather code from earlier to use it.

It looks much better, but now we're running into an issue. Can you spot it? In the second .then we want to call updateUI . The problem is we need to pass updateUI both the user and the weather . Currently, how we have it set up, we're only receiving the weather , not the user . Somehow we need to figure out a way to make it so the promise that getWeather returns is resolved with both the user and the weather .

Here's the key. resolve is just a function. Any arguments you pass to it will be passed along to the function given to .then . What that means is that inside of getWeather , if we invoke resolve ourself, we can pass to it weather and user . Then, the second .then method in our chain will receive both user and weather as an argument.

You can play around with the final code here

It's in our click handler where you really see the power of promises shine compared to callbacks.

Following that logic feels natural because it's how we're used to thinking, sequentially. getUser then getWeather then update the UI with the data .

Now it's clear that promises drastically increase the readability of our asynchronous code, but is there a way we can make it even better? Assume that you were on the TC39 committee and you had all the power to add new features to the JavaScript language. What steps, if any, would you take to improve this code?

As we've discussed, the code reads pretty nicely. Just as our brains work, it's in a sequential order. One issue that we did run into was that we needed to thread the data ( users ) from the first async request all the way through to the last .then . This wasn't a big deal, but it made us change up our getWeather function to also pass along users . What if we just wrote our asynchronous code the same way which we write our synchronous code? If we did, that problem would go away entirely and it would still read sequentially. Here's an idea.

Well, that would be nice. Our asynchronous code looks exactly like our synchronous code. There's no extra steps our brain needs to take because we're already very familiar with this way of thinking. Sadly, this obviously won't work. As you know, if we were to run the code above, user and weather would both just be promises since that's what getUser and getWeather return. But remember, we're on TC39. We have all the power to add any feature to the language we want. As is, this code would be really tricky to make work. We'd have to somehow teach the JavaScript engine to know the difference between asynchronous function invocations and regular, synchronous function invocations on the fly. Let's add a few keywords to our code to make it easier on the engine.

First, let's add a keyword to the main function itself. This could clue the engine to the fact that inside of this function, we're going to have some asynchronous function invocations. Let's use async for this.

Cool. That seems reasonable. Next let's add another keyword to let the engine know exactly when a function being invoked is asynchronous and is going to return a promise. Let's use await . As in, "Hey engine. This function is asynchronous and returns a promise. Instead of continuing on like you typically do, go ahead and 'await' the eventual value of the promise and return it before continuing". With both of our new async and await keywords in play, our new code will look like this.

Pretty slick. We've invented a reasonable way to have our asynchronous code look and behave as if it were synchronous. Now the next step is to actually convince someone on TC39 that this is a good idea. Lucky for us, as you probably guessed by now, we don't need to do any convincing because this feature is already part of JavaScript and it's called Async/Await .

Don't believe me? Here's our live code now that we've added Async/Await to it. Feel free to play around with it.

async functions return a promise

Now that you've seen the benefit of Async/Await, let's discuss some smaller details that are important to know. First, anytime you add async to a function, that function is going to implicitly return a promise.

Even though getPromise is literally empty, it'll still return a promise since it was an async function.

If the async function returns a value, that value will also get wrapped in a promise. That means you'll have to use .then to access it.

await without async is bad

If you try to use the await keyword inside of a function that isn't async , you'll get an error.

Here's how I think about it. When you add async to a function it does two things. It makes it so the function itself returns (or wraps what gets returned in) a promise and makes it so you can use await inside of it.

Error Handling

You may have noticed we cheated a little bit. In our original code we had a way to catch any errors using .catch . When we switched to Async/Await, we removed that code. With Async/Await, the most common approach is to wrap your code in a try/catch block to be able to catch the error.

Before you leave

I know, another newsletter pitch - but hear me out. Most JavaScript newsletters are terrible. When’s the last time you actually looked forward to getting one? Even worse, when’s the last time you actually read one? We wanted to change that.

We call it Bytes , but others call it their favorite newsletter .

Delivered to 216,061 developers every Monday and Thursday

Avatar for @sduduzo_g

@ sduduzo_g

This is the first ever newsletter that I open a music playlist for and maximize my browser window just to read it in peace. Kudos to @uidotdev for great weekly content.

Avatar for @flybayer

Brandon Bayer

The Bytes newsletter is a work of art! It’s the only dev newsletter I’m subscribed too. They somehow take semi boring stuff and infuse it with just the right amount of comedy to make you chuckle.

Avatar for @johnhawly

John Hawley

@ johnhawly

Bytes has been my favorite newsletter since its inception. It’s my favorite thing I look forward to on Mondays. Goes great with a hot cup of coffee!

Avatar for @garrettgreen

Garrett Green

@ garrettgreen

I subscribe to A LOT of dev (especially JS/TS/Node) newsletters and Bytes by @uidotdev is always such a welcomed, enjoyable change of pace to most (funny, lighthearted, etc) but still comprehensive/useful.

Avatar for @mhashim6_

@ mhashim6_

Literally the only newsletter I’m waiting for every week.

Avatar for @graysonhicks

Grayson Hicks

@ graysonhicks

Bytes is the developer newsletter I most look forward to each week. Great balance of content and context! Thanks @uidotdev.

Avatar for @mitchellbwright

Mitchell Wright

@ mitchellbwright

I know I’ve said it before, but @tylermcginnis doesn’t miss with the Bytes email. If you’re a developer, you really need to subscribe

Avatar for @aspittel

Ali Spittel

Can I just say that I giggle every time I get the @uidotdev email each week? You should definitely subscribe.

Avatar for @thefinnomenon

@ thefinnomenon

Every JavaScript programmer should be subscribed to the newsletter from @uidotdev. Not only do they manage to succinctly cover the hot news in the JavaScript world for the week but it they manage to add a refreshing humor to it all.

Understanding Async & Await

Learning async/await can be daunting. It's something you've been procrastinating on for weeks, if not months!

You might know the difference between synchronous and asynchronous code, but you're completely lost in the application of it.

At this rate, it's hard to imagine how you'll ever wrap your head around asynchronous JavaScript . 😫

But learning async/await doesn't have to be scary!

With a little guidance and the right approach, you can write modern asynchronous code that just works . Not next month, or next week, but today .

It's true, async/await is unintuitive at first.. but only because you haven't built a mental model for it, yet !

To understand async/await we have to talk a little bit about promises first. Since async/await is built on top of promises, when you're writing async/await you're in fact dealing with promises under the hood.

What's a Promise ?

Put simply, a Promise is a JavaScript object that represents a future value. It holds two pieces of information: the promise state , and a value or error (depending on the state).

A promise can be in one of these states:

  • pending — initial state, the operation is in progress
  • fulfilled — the operation completed successfully
  • rejected — the operation failed

A promise is settled when the promise has either fulfilled or rejected, but not pending. A promise eventually fulfills with a value or rejects with an error (or reason).

A function that returns a promise is by definition an asynchronous function. (remember this, we'll come back to it later)

Even though the promise is returned synchronously, the value inside the promise is asynchronous and can only be accessed after the promise has fulfilled.

What does await actually do?

When you add the await keyword in front of a promise you instruct the JavaScript runtime to pause execution inside the current function, wait until the promise settles, and only after the promise has settled, continue executing the rest of the code inside that function.

Await guarantees that you have access to an asynchronous value past a certain point. The runtime won't execute any further code inside the function until the promise fulfills with the value (or rejects with an error).

One thing to keep in mind is that starting an asynchronous operation and awaiting its result are two separate actions . This becomes clear when you write them on different lines.

Therefore, await is solely responsible for pausing further execution until the asynchronous operation has completed. It is not what starts the asynchronous operation.

How about async ?

The async keyword grants a special ability to a function.

A function marked as async can pause execution anywhere inside its body. In other words, the function is allowed to use the await keyword. Await can only be used inside async functions. (you'll understand why in a bit)

An async function has two important properties:

  • It always returns a Promise
  • You can use the await keyword inside its scope

When you return a non -Promise value inside an async function, the value is wrapped inside a Promise before being returned.

Remember what I said earlier about functions that return a promise — they're by definition asynchronous. Therefore, a function marked as async is always asynchronous.

A non- async function that returns a promise is also asynchronous. The following code is practically the same as the one above:

All other functions are considered synchronous functions.

Why async & await are inseparable

Knowing that await pauses execution inside a function and that a non-async function is synchronous if it doesn't return a promise, you can start to see why the following code doesn't work:

How can getFive() synchronously return five if it needs to wait for fetchFive() first?

Put differently, how can you wait for an asynchronous value and then proceed to return it synchronously?

You simply can't .

That's why await can only be used inside a function declared with the async keyword. An async function wraps the return value in a promise which makes the value asynchronous.

Hopefully you have a better understanding of async/await by now. Go ahead and use it in your projects!

Master Asynchronous JavaScript 🚀

Learn how to write modern and easy-to-read asynchronous code with a FREE 5-day email course .

Through visual graphics you will learn how to decompose async code into individual parts and put them back together using a modern async/await approach. Moreover, with 30+ real-world exercises you'll transform knowledge into a practical skill that will make you a better developer.

Get Lesson 1 now 👇🏼

You'll also get tips on building scalable Node.js applications about twice a month. I respect your email privacy. Unsubscribe any time.

You might also like

Avoid this mistake when caching asynchronous results, why async/await inside foreach is a bad idea, a visual guide to refactoring callback functions to promises & async/await.

  • Skip to main content
  • Skip to search
  • Skip to select language
  • Sign up for free
  • Português (do Brasil)

Baseline Widely available

This feature is well established and works across many devices and browser versions. It’s been available across browsers since April 2017 .

  • See full compatibility
  • Report feedback

The await operator is used to wait for a Promise and get its fulfillment value. It can only be used inside an async function or at the top level of a module .

A Promise , a thenable object , or any value to wait for.

Return value

The fulfillment value of the promise or thenable object, or, if the expression is not thenable, the expression's own value.

Throws the rejection reason if the promise or thenable object is rejected.

Description

await is usually used to unwrap promises by passing a Promise as the expression . Using await pauses the execution of its surrounding async function until the promise is settled (that is, fulfilled or rejected). When execution resumes, the value of the await expression becomes that of the fulfilled promise.

If the promise is rejected, the await expression throws the rejected value. The function containing the await expression will appear in the stack trace of the error. Otherwise, if the rejected promise is not awaited or is immediately returned, the caller function will not appear in the stack trace.

The expression is resolved in the same way as Promise.resolve() : it's always converted to a native Promise and then awaited. If the expression is a:

  • Native Promise (which means expression belongs to Promise or a subclass, and expression.constructor === Promise ): The promise is directly used and awaited natively, without calling then() .
  • Thenable object (including non-native promises, polyfill, proxy, child class, etc.): A new promise is constructed with the native Promise() constructor by calling the object's then() method and passing in a handler that calls the resolve callback.
  • Non-thenable value: An already-fulfilled Promise is constructed and used.

Even when the used promise is already fulfilled, the async function's execution still pauses until the next tick. In the meantime, the caller of the async function resumes execution. See example below.

Because await is only valid inside async functions and modules, which themselves are asynchronous and return promises, the await expression never blocks the main thread and only defers execution of code that actually depends on the result, i.e. anything after the await expression.

Awaiting a promise to be fulfilled

If a Promise is passed to an await expression, it waits for the Promise to be fulfilled and returns the fulfilled value.

Thenable objects

Thenable objects are resolved just the same as actual Promise objects.

They can also be rejected:

Conversion to promise

If the value is not a Promise , await converts the value to a resolved Promise , and waits for it. The awaited value's identity doesn't change as long as it doesn't have a then property that's callable.

Handling rejected promises

If the Promise is rejected, the rejected value is thrown.

You can handle rejected promises without a try block by chaining a catch() handler before awaiting the promise.

This is built on the assumption that promisedFunction() never synchronously throws an error, but always returns a rejected promise. This is the case for most properly-designed promise-based functions, which usually look like:

However, if promisedFunction() does throw an error synchronously, the error won't be caught by the catch() handler. In this case, the try...catch statement is necessary.

Top level await

You can use the await keyword on its own (outside of an async function) at the top level of a module . This means that modules with child modules that use await will wait for the child modules to execute before they themselves run, all while not blocking other child modules from loading.

Here is an example of a simple module using the Fetch API and specifying await within the export statement. Any modules that include this will wait for the fetch to resolve before running any code.

Control flow effects of await

When an await is encountered in code (either in an async function or in a module), the awaited expression is executed, while all code that depends on the expression's value is paused and pushed into the microtask queue . The main thread is then freed for the next task in the event loop. This happens even if the awaited value is an already-resolved promise or not a promise. For example, consider the following code:

In this case, the two async functions are synchronous in effect, because they don't contain any await expression. The three statements happen in the same tick. In promise terms, the function corresponds to:

However, as soon as there's one await , the function becomes asynchronous, and execution of following statements is deferred to the next tick.

This corresponds to:

While the extra then() handler is not necessary, and the handler can be merged with the executor passed to the constructor, the then() handler's existence means the code will take one extra tick to complete. The same happens for await . Therefore, make sure to use await only when necessary (to unwrap promises into their values).

Other microtasks can execute before the async function resumes. This example uses queueMicrotask() to demonstrate how the microtask queue is processed when each await expression is encountered.

In this example, the test() function is always called before the async function resumes, so the microtasks they each schedule are always executed in an intertwined fashion. On the other hand, because both await and queueMicrotask() schedule microtasks, the order of execution is always based on the order of scheduling. This is why the "queueMicrotask() after calling async function" log happens after the async function resumes for the first time.

Improving stack trace

Sometimes, the await is omitted when a promise is directly returned from an async function.

However, consider the case where lastAsyncTask asynchronously throws an error.

Only lastAsyncTask appears in the stack trace, because the promise is rejected after it has already been returned from noAwait — in some sense, the promise is unrelated to noAwait . To improve the stack trace, you can use await to unwrap the promise, so that the exception gets thrown into the current function. The exception will then be immediately wrapped into a new rejected promise, but during error creation, the caller will appear in the stack trace.

Contrary to some popular belief, return await promise is at least as fast as return promise , due to how the spec and engines optimize the resolution of native promises. There's a proposal to make return promise faster and you can also read about V8's optimization on async functions . Therefore, except for stylistic reasons, return await is almost always preferable.

Specifications

Specification

Browser compatibility

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

  • async function
  • async function expression
  • AsyncFunction
  • Top-level await on v8.dev (2019)
  • typescript-eslint rule: return-await

Home » Python Concurrency » Python async/await

Python async/await

Summary : in this tutorial, you will learn about Python coroutines and how to use the Python async and await keywords to create and pause coroutines.

Introduction to Python coroutines

A coroutine is a regular function with the ability to pause its execution when encountering an operation that may take a while to complete.

When the long-running operation completes, you can resume the paused coroutine and execute the remaining code in that coroutine.

While the coroutine is waiting for the long-running operation, you can run other code. By doing this, you can run the program asynchronously to improve its performance.

To create and pause a coroutine, you use the Python async and await keywords:

  • The async keyword creates a coroutine.
  • The await keyword pauses a coroutine.

Defining a coroutine with Python async keyword

The following defines a simple function that returns the square number of an integer:

And you can pass an integer to the square() function to get its square number:

When you add the async keyword to the function, the function becomes a coroutine:

A calling coroutine returns a coroutine object that will be run later. For example:

In this example, we call the square() coroutine, assign the returned value to the result variable, and print it out.

When you call a coroutine, Python doesn’t execute the code inside the coroutine immediately. Instead, it returns a coroutine object.

The second line in the output also shows an error message indicating that the coroutine was never awaited. More on this in the following await section:

To run a coroutine, you need to execute it on an event loop . Prior to Python 3.7, you have to manually create an event loop to execute coroutines and close the event loop.

However, since version 3.7, the asyncio library added some functions that simplify the event loop management.

For example, you can use the asyncio.run() function to automatically create an event loop, run a coroutine, and close it.

The following uses the asyncio.run() function to execute the square() coroutine and get the result:

It’s important to note that the asyncio.run() is designed to be the main entry point of an asyncio program.

Also, the asyncio.run() function only executes one coroutine which may call other coroutines and functions in the program.

Pausing a coroutine with Python await keyword

The await keyword pauses the execution of a coroutine. The await keyword is followed by a call to a coroutine like this:

The await keyword causes the my_coroutine() to execute, waits for the code to be completed, and returns a result.

It’s important to note that await keyword is only valid inside a coroutine. In other words, you must use the await keyword inside a coroutine.

This is the reason why you saw an error message in the above example that uses the await keyword outside of a coroutine.

The following example shows how to use the await keyword to pause a coroutine:

How it works. (we’ll focus on the main() function):

First, call the square() coroutine using the await keyword. The await keyword will pause the execution of the main() coroutine, wait for the square() coroutine to complete, and return the result:

Second, call the square() coroutine a second time using the await keyword:

Third, display the total:

The following statement uses the run() function to execute the main() coroutine and manage the event loop:

So far, our program executes like a synchronous program. It doesn’t reveal the power of the asynchronous programming model.

  • A coroutine is a regular function with the power of pausing a long-running operation, waiting for the result, and resuming from the paused point.
  • Use async keyword to define a coroutine.
  • Use await keyword to pause a coroutine.
  • Use asyncio.run() function to automatically execute a coroutine on an event loop and manage an event loop.

Python async/await Tutorial

await job assignment

Asynchronous programming has been gaining a lot of traction in the past few years, and for good reason. Although it can be more difficult than the traditional linear style, it is also much more efficient.

For example, instead of waiting for an HTTP request to finish before continuing execution, with Python async coroutines you can submit the request and do other work that's waiting in a queue while waiting for the HTTP request to finish. It might take a bit more thinking to get the logic right, but you'll be able to handle a lot more work with less resources.

Even then, the syntax and execution of asynchronous functions in languages like Python actually aren't that hard. Now, JavaScript is a different story, but Python seems to execute it fairly well.

Asynchronicity seems to be a big reason why Node.js so popular for server-side programming. Much of the code we write, especially in heavy IO applications like websites, depends on external resources. This could be anything from a remote database call to POSTing to a REST service. As soon as you ask for any of these resources, your code is waiting around with nothing to do.

With asynchronous programming, you allow your code to handle other tasks while waiting for these other resources to respond.

An asynchronous function in Python is typically called a 'coroutine', which is just a function that uses the async keyword, or one that is decorated with @asyncio.coroutine . Either of the functions below would work as a coroutine and are effectively equivalent in type:

These are special functions that return coroutine objects when called. If you're familiar with JavaScript Promises, then you can think of this returned object almost like a Promise. Calling either of these doesn't actually run them, but instead a coroutine object is returned, which can then be passed to the event loop to be executed later on.

In case you ever need to determine if a function is a coroutine or not, asyncio provides the method asyncio.iscoroutinefunction(func) that does exactly this for you. Or, if you need to determine if an object returned from a function is a coroutine object, you can use asyncio.iscoroutine(obj) instead.

There are a few ways to actually call a coroutine, one of which is the yield from method. This was introduced in Python 3.3, and has been improved further in Python 3.5 in the form of async/await (which we'll get to later).

The yield from expression can be used as follows:

As you can see, yield from is being used within a function decorated with @asyncio.coroutine . If you were to try and use yield from outside this function, then you'd get error from Python like this:

In order to use this syntax, it must be within another function (typically with the coroutine decorator).

  • Async/await

The newer and cleaner syntax is to use the async/await keywords. Introduced in Python 3.5, async is used to declare a function as a coroutine, much like what the @asyncio.coroutine decorator does. It can be applied to the function by putting it at the front of the definition:

To actually call this function, we use await , instead of yield from , but in much the same way:

Again, just like yield from , you can't use this outside of another coroutine, otherwise you'll get a syntax error.

In Python 3.5, both ways of calling coroutines are supported, but the async/await way is meant to be the primary syntax.

  • Running the event loop

None of the coroutine stuff I described above will matter (or work) if you don't know how to start and run an event loop . The event loop is the central point of execution for asynchronous functions, so when you want to actually execute the coroutine, this is what you'll use.

The event loop provides quite a few features to you:

  • Register, execute, and cancel delayed calls (asynchronous functions)
  • Create client and server transports for communication
  • Create subprocesses and transports for communication with another program
  • Delegate function calls to a pool of threads

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

While there are actually quite a few configurations and event loop types you can use, most of the programs you write will just need to use something like this to schedule a function:

The last three lines are what we're interested in here. It starts by getting the default event loop ( asyncio.get_event_loop() ), scheduling and running the async task, and then closing the loop when the loop is done running.

The loop.run_until_complete() function is actually blocking, so it won't return until all of the asynchronous methods are done. Since we're only running this on a single thread, there is no way it can move forward while the loop is in progress.

Now, you might think this isn't very useful since we end up blocking on the event loop anyway (instead of just the IO calls), but imagine wrapping your entire program in an async function, which would then allow you to run many asynchronous requests at the same time, like on a web server.

You could even break off the event loop in to its own thread, letting it handle all of the long IO requests while the main thread handles the program logic or UI.

Okay, so let's see a slightly bigger example that we can actually run. The following code is a pretty simple asynchronous program that fetches JSON from Reddit, parses the JSON, and prints out the top posts of the day from /r/python, /r/programming, and /r/compsci.

The first method shown, get_json() , is called by get_reddit_top() and just creates an HTTP GET request to the appropriate Reddit URL. When this is called with await , the event loop can then continue on and service other coroutines while waiting for the HTTP response to get back. Once it does, the JSON is returned to get_reddit_top() , gets parsed, and is printed out.

This is a bit different than the sample code we showed earlier. In order to get multiple coroutines running on the event loop, we're using asyncio.ensure_future() and then run the loop forever to process everything.

To run this, you'll need to install aiohttp first, which you can do with PIP:

Now just make sure you run it with Python 3.5 or higher, and you should get an output like this:

Notice that if you run this a few times, the order in which the subreddit data is printed out changes. This is because each of the calls we make releases (yields) control of the thread, allowing another HTTP call to process. Whichever one returns first gets printed out first.

Although Python's built-in asynchronous functionality isn't quite as smooth as JavaScript's, that doesn't mean you can't use it for interesting and efficient applications. Just take 30 minutes to learn its ins and outs and you'll have a much better sense as to how you can integrate this in to your own applications.

What do you think of Python's async/await? How have you used it in the past? Let us know in the comments!

You might also like...

  • How to Send Emails with Gmail using Python
  • Validating and Formatting Phone Numbers in Python with phonenumbers
  • Test Driven Development with pytest
  • How to Exploit the Heartbleed Bug

Improve your dev skills!

Get tutorials, guides, and dev jobs in your inbox.

No spam ever. Unsubscribe at any time. Read our Privacy Policy.

In this article

await job assignment

Monitor with Ping Bot

Reliable monitoring for your app, databases, infrastructure, and the vendors they rely on. Ping Bot is a powerful uptime and performance monitoring tool that helps notify you and resolve issues before they affect your customers.

OpenAI

Vendor Alerts with Ping Bot

Get detailed incident alerts about the status of your favorite vendors. Don't learn about downtime from your customers, be the first to know with Ping Bot.

Supabase

© 2013- 2024 Stack Abuse. All rights reserved.

Get the Reddit app

Welcome to Moscow!

How to look for work in Moscow?

I currently live in the Netherlands. I'm looking for a long-term stable job in Moscow that comes with a work-visa.

What are the best ways to look for a job with work-visa in Moscow for someone that is currently in Western Europe?

So far I've tried websites like hh.ru , however there are over 16 million resumes on the site, so standing out on such sites is a challenge, even with a good resume.

I'll be in Moscow for a short trip from May 7th until May 14th, so during that time I could see potential employers.

I'm looking for advice and tips.

And if you have a job offer, then that would be even better.

By continuing, you agree to our User Agreement and acknowledge that you understand the Privacy Policy .

Enter the 6-digit code from your authenticator app

You’ve set up two-factor authentication for this account.

Enter a 6-digit backup code

Create your username and password.

Reddit is anonymous, so your username is what you’ll go by here. Choose wisely—because once you get a name, you can’t change it.

Reset your password

Enter your email address or username and we’ll send you a link to reset your password

Check your inbox

An email with a link to reset your password was sent to the email address associated with your account

Choose a Reddit account to continue

  • Stack Overflow for Teams Where developers & technologists share private knowledge with coworkers
  • Advertising & Talent Reach devs & technologists worldwide about your product, service or employer brand
  • OverflowAI GenAI features for Teams
  • OverflowAPI Train & fine-tune LLMs
  • Labs The future of collective knowledge sharing
  • About the company Visit the blog

Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Get early access and see previews of new features.

Does await guarantee execution order without assignment in JavaScript?

Subject. Can I say that two pieces of code below are equal:

I tried and it looks like they are equal but there is a doubt(e.g. some optimization)

  • async-await

kharandziuk's user avatar

  • No, optimisation must not mess with semantics. –  Bergi Commented Oct 12, 2017 at 14:34
  • Is it safe to use it without assignment? As in: await somePromiseToresolve(); return something; –  Amiga500 Commented Jul 12, 2018 at 8:02

2 Answers 2

To expand on Dan D's answer (because it took me a while to figure out myself), I'll say a few more things about execution flow. Indeed, using await blocks the flow of the method it's called in until it resolves. Let's say we have this async function:

So if we call with await, like this:

we get the following result:

However, when we use .then() :

this happens:

This is because .then() does not stops the execution flow and runs next function in the chain only when the previous promise is finished. Sometimes you want this to happen, sometimes you don't, sometimes it doesn't matters. But if you don't know about this, it can take some time to figure it out. So I hope this example will help you understand it.

Almighty Baka's user avatar

Yes they are exactly the same, it's more or less syntactic sugar. The await causes execution to pause until the awaited Promise is resolved.

See Javascript async the section on rewriting a promise chain for more information.

Dan D's user avatar

  • the thing: this section misses the case without assignment. But I believe you are right –  kharandziuk Commented Oct 12, 2017 at 14:40

Your Answer

Reminder: Answers generated by artificial intelligence tools are not allowed on Stack Overflow. Learn more

Sign up or log in

Post as a guest.

Required, but never shown

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy .

Not the answer you're looking for? Browse other questions tagged javascript node.js async-await or ask your own question .

  • Featured on Meta
  • We've made changes to our Terms of Service & Privacy Policy - July 2024
  • Announcing a change to the data-dump process

Hot Network Questions

  • Backshifting tenses in reported speech
  • Who was Dionysodorus of Melos?
  • Thomson's lamp: a useless paradox?
  • When Beatrix stops placing dominoes on a 5x5 board, what is the largest possible number of squares that may still be uncovered?
  • Why do tip vortices seem to 'bend' inwards at the tip of a plane wing?
  • Does the notion of "moral progress" presuppose the existence of an objective moral standard?
  • What is this tree with these red berries?
  • Why is the E in 'collega' long?
  • Split column into lines using awk
  • What is the “Reinforcement Axiom” for a social choice function?
  • How can DC charge a capacitor?
  • Borderlands 2 for PS3 has unbelievably low drop rates. Is something wrong?
  • parallelStream with collect method()
  • Lexicographically earliest permutation of the initial segment of nonnegative integers subject to divisibility constraints
  • How should I acknowledge a reviewer who left a negative review?
  • If energy can be converted into matter and interact with it, would this imply that matter and energy are the same "ontological substance"?
  • Can I replace this resistor (in USB adapter that plugs to mains) with this other resistor?
  • Trimming direction on climb out and descent to maintain an airspeed
  • Is it realistic that I can fit and maintain 22 trillion people without the need to eat in East Asia?
  • Please “weigh in” on my shelf weight question
  • Can I use the CJKfonts as math fonts?
  • What is the “history“ of mindfulness
  • Does a router lookup in routing table twice for each packet?
  • Why have SIGPIPE when EPIPE exists?

await job assignment

IMAGES

  1. "TO AWAIT JOB ASSIGNMENT" in Cantonese (待業)

    await job assignment

  2. Recruiting Basics for BMC

    await job assignment

  3. Work Assignment Gratis

    await job assignment

  4. 6 Steps to Write Your Assignment Perfectly

    await job assignment

  5. 5 Key Benefits of taking Assignment Writing help from experts

    await job assignment

  6. If you accept a job assignment from Hire Priority, you are expected to

    await job assignment

VIDEO

  1. DEADLY AMBUSH

COMMENTS

  1. How JavaScript Async/Await Works and How to Use It

    This function will be async. It will use JavaScript fetch() API to fetch the GitHub API and wait for the response. When the response arrives, the async function will translate received data to JSON format and return the result. Since this is an async function the data will be returned in the form of a promise.

  2. What is correct way to combine long-running tasks with async / await

    Nov 30, 2013 at 0:33. Actually, now that you've changed the return type of ExecuteAsync to Task, passing async () => await ExecuteAsync lambda to Task.Factory.StartNew would work, but it's redundant. Just pass ExecuteAsync and do task.Unwrap on the Task<Task> object returned by Task.Factory.StartNew. - noseratio.

  3. publications

    6. First, congratulations!! "Awaiting Assignment to Batch" is part of the accepted workflow. Once accepted, some journals have a set of tasks that a manuscript goes through. One of the steps available (and being used here) is batching and sending of the manuscript to the journals production service. It will wait until a predetermined condition ...

  4. Async/Await Explained with Diagrams and Examples

    int x = await BarAsync();. This is the normal way of calling an async method:. FooAsync() calls BarAsync() BarAsync() encounters the await Task.Delay(2000); and returns an incomplete task to FooAsync(), which returns the incomplete task to its caller. Later, BarAsync() completes and returns 7 to FooAsync() which stores the 7 in variable x. FooAsync() continues running now that it has a value ...

  5. Mastering Asynchronous JavaScript: Promises, async/await, Error

    With async/await, we can write asynchronous operations that look and feel like traditional synchronous code, unlocking a whole new level of simplicity and elegance. The async/await syntax revolves around two keywords: async and await. By marking a function with the async keyword, we indicate that it contains asynchronous operations.

  6. A guide to async/await in TypeScript

    Awaited is a utility type that models operations like await in async functions. It unwraps the resolved value of a promise, discarding the promise itself, and works recursively, thereby removing any nested promise layers as well. Awaited is the type of value that you expect to get after awaiting a promise.

  7. Async JavaScript: From Callbacks, to Promises, to Async/Await

    Now that you've seen the benefit of Async/Await, let's discuss some smaller details that are important to know. First, anytime you add async to a function, that function is going to implicitly return a promise. async function getPromise() {} const promise = getPromise();

  8. Understanding Async & Await

    A function marked as async can pause execution anywhere inside its body. In other words, the function is allowed to use the await keyword. Await can only be used inside async functions. (you'll understand why in a bit) An async function has two important properties: It always returns a Promise.

  9. await

    await is usually used to unwrap promises by passing a Promise as the expression. Using await pauses the execution of its surrounding async function until the promise is settled (that is, fulfilled or rejected). When execution resumes, the value of the await expression becomes that of the fulfilled promise. If the promise is rejected, the await ...

  10. Python Async/Await

    Summary: in this tutorial, you will learn about Python coroutines and how to use the Python async and await keywords to create and pause coroutines.. Introduction to Python coroutines. A coroutine is a regular function with the ability to pause its execution when encountering an operation that may take a while to complete.. When the long-running operation completes, you can resume the paused ...

  11. Python async/await Tutorial

    This was introduced in Python 3.3, and has been improved further in Python 3.5 in the form of async/await (which we'll get to later). The yield from expression can be used as follows: import asyncio. @asyncio.coroutine def get_json(client, url): file_content = yield from load_file( '/Users/scott/data.txt' )

  12. to await job assignment (term used only in mainland China)

    Sample sentences with "to await job assignment (term used only in mainland China)" Declension Stem . Match words . all exact any . In November # the Administration decided to review the comparative analysis mechanism, which was aimed at lessening the effect of job eliminations on the number of staff awaiting new assignments.

  13. Question on getting out of Awaiting Assignment : r/Raytheon

    The best way to get off AA is to be on AA because your manager eventually will put you to work when his butt is getting chewed out by his manager from you being on AA. -7. Reply. Share. FelCandyArt. • 9 mo. ago. Book a cube onsite and meet people, ask questions, attend meetings, and offer help. Be a bit proactive and learn as much as you can.

  14. Chapter 10 International Training and Management Development ...

    The careers of high-potential employees and their job assignments need to be closely monitored and managed to ensure that a. they experience job assignments of adequate variety b. they experience job assignments of appropriate challenge and length c. they learn how to achieve results in new settings d. all of the above.

  15. How to look for work in Moscow? : r/Moscow

    I currently live in the Netherlands. I'm looking for a long-term stable job in Moscow that comes with a work-visa. What are the best ways to look for…

  16. The procedure for receiving the work visa for foreign citizens

    Copy of both sides of "Work permit of foreign citizen" (Plastic card) with the presentation of original; Copy of the page of passport of foreign citizen, which contains photo and specifications (1 copy); Copy of labor contract; Receipt for state fee payment. Application of foreign citizen to the Russian consulate with purpose of obtaining ...

  17. 'Trump's frustration is clear': Burnett reacts to Trump's ...

    CNN's Erin Burnett reacts to former President Donald Trump's comments following the release of two dozen detainees as part of the biggest prisoner exchange between Russia and the West since ...

  18. c#

    But when I do a review on my own code (trying using other syntax to do the same job), I suddenly figure out the following two are different. public static async Task dosthA() { //This will be working synchronously, take 3 seconds. await sleep(); await sleep(); await sleep(); //This will be working asynchronously, take 1 second only.

  19. Careers and Job Openings

    Toy Drive benefitting Toys for Tots. Oregon, February 2023. Volunteers with Oregon's Parks department. Washington, May 2022. Camp Korey for a day of service. We're hiring! Browse available jobs and current open positions at Ziply Fiber. We are based in Kirkland, Washington and serve communities across Washington, Oregon, Idaho and Montana.

  20. node.js

    Yes they are exactly the same, it's more or less syntactic sugar. The await causes execution to pause until the awaited Promise is resolved. See Javascript async the section on rewriting a promise chain for more information. the thing: this section misses the case without assignment.