|Written by Ian Elliot|
|Monday, 01 January 2018|
Page 1 of 2
Now Available as a Print Book:
You can buy it from: Amazon
If you are a novice to intermediate programmer then you can simply use async and await as if they were magic designed to solve all of your problems. Indeed one danger is that innocent programmers might grow so comfortable with asynchronous programming that they might forget that is what it is.
While async and await make asynchronous programming easy and less error-prone, if you are going to use it in creative or indeed in any way but the most basic, a thorough understanding what is going on is very important.
Async Pretends To Be Sync
With the Promise we almost have everything we need to make asynchronous code really easy to use. Indeed we have almost enough to make it look exactly like synchronous code.
For example, suppose you have a long running function that operates asynchronously and returns a Promise, then at the movement you might use it something like:
The then method provides the code that is to be run after the Promise is settled.
This good, but how much easier it would be if we could write:
This would keep the natural order of the code.
Of course you can’t just wait.
However, suppose the wait could be implemented so that it freed the UI thread and allowed it to process any events, only returning to the code when the slowFunction had completed. This would allow us to write the code as if it was synchronous and still keep the UI serviced.
This sounds easy, but for it to be useful the wait would have to ensure that the state was saved when the UI thread was doing something else, and was restored when the code restarted. For example if a for loop was in progress then the state of the for loop would have to be preserved and restarted after the wait.
What is interesting is that we already have a command that can save the state and resume code – the yield instruction. As already described, see Chapter 4, yield was introduced to allow generators to be implemented. However, it can also be used to save the current state. In the early days yield was, and still is, used to implement facilities that are now provided by async and await. You could basically do something like:
Of course, you would have to arrange that the yield freed the UI thread and that the slow Function restarted the code again. This is the basis of the polyfills used to provide async and await in environments that don’t support it.
Let's now see how the real async and await work.
There are two parts to async and await, and while they work together it is important to understand what each one does.
Let’s start with async.
If you apply the async keyword to a function, it is converted into an AsyncFunction object with the same code.
An AsyncFunction object is a special type of function that can be suspended to wait for an async operation using the await command.
Put more simply: you can only use await within a function that is marked as async.
Notice that you don’t have to use an await within an AsyncFunction, but there isn’t much point in creating one if you don’t.
converts the function into an AsyncFunction object stored in myFunc with the same code.
An AsyncFunction object has the same code, but with one big difference – it returns a Promise.
The Promise is created automatically when the function starts executing. The Promise is returned with whatever the original function returned as its resolved value. If the function returns a Promise then the new Promise simply reflects its state. If the function doesn’t return a value then the Promise has a resolved value of undefined.
So if you try:
you will see not 3, but [object Promise].
If you need the resolved value of the Promise you have to use its then method:
So this is the first rule of async/await:
It is also true that, for a range of reasons, you can’t use async in the top level code because it always has to be applied to a function.
In practice you don’t have to use a function expression to create an async function, a standard function declaration will do.
works in exactly the same way.
You can also use async to convert a value into a Promise that wraps that value. For example:
converts v into p, a Promise which resolves to it. Notice the final pair of parentheses - this is an immediately-invoked function expression, IIFE.
The second rule of async/await is:
This is simply because only an AsyncFunction has the necessary extras to allow it to be suspended and resumed.
The await operator can be applied to any Promise and this pauses the function’s execution until the Promise is fulfilled. That is, it really does await the Promise’s fulfillment.
Thus await converts a Promise into a result or an exception just as if it was a result from a synchronous function.
If you use await on a value that isn’t a Promise, it simply returns that value at once without pausing the function’s execution.
You should now be able to see how it all fits together.
Within an AsyncFunction you can write code as it if was synchronous by waiting for each Promise to resolve.
This first waits for one second and then for two seconds. Notice that the Promise returned by the delay is “unwrapped” and the random value is stored in first and then second. Also notice that as an async function, myFunction returns a Promise so to call it from the top level you would have to use:
This is the basic idea of using async and await, but there are some subtle points we need to consider if we are really going to master what is going on.
The main issue is what does the thread do while the async function is awaiting and when exactly does the function resume?
Note: the delay function was introduced in Chapter 6 and is:
|Last Updated ( Wednesday, 03 January 2018 )|