- In this section, we're going to see how to use
the async and the await keywords to simplify how we deal
with Promises in our code and in our tests.
So most code these days that uses Promises
they make use of the async keyword to simplify
waiting for the result to come back.
And the async keyword, which is also has a role to play.
So we'll have a look in this section at how we can use async
and await in our code when we are dealing with Promises
and also in our tests as well.
If you want to follow along all the code for this section
is in this folder TestingPromises_AsyncAwait.
Okay then, let's have a think.
So the await keyword. This is the idea.
Here is first of all, the mechanism.
When you call a function that turns a Promise
you can say a Promise.then
and then you can have some code that gets executed
in the then callback.
An alternative to that is to prefix the function call.
You call a function that returns a Promise,
prefix that function call with the await keyword.
What will happen is when you call the function
the await keyword, basically it takes the remainder
of the code in that function from that point onwards
is kind of moved into a giant then callback.
It'll only be executed when the Promise has resolved.
You can also have a catch handler
like a literally a tri catch block
in case the Promise got rejected.
So that's the await keyword.
The async keyword is less important.
You still need it.
Basically if you use the await keyword, the Promises
then you have to indicate on the function itself
you have to prefix the function with the asymc keyword
so that when you code runs, it's kind of,
it knows that it's asynchronous function
and it can then expect the await keyword
inside your function.
So the two keywords go together.
So have a look at this due task function.
We saw this example earlier.
What it does, if you remember I call this function
and I give it a task number, like one or two or three,
and a random number between zero and one.
And inside here, it creates and returns a Promise object.
Inside the constructor I give it some code to execute
a long running task.
This function here will take one to five seconds
typically to execute.
And eventually this function will either resolve
the Promise successfully or reject the Promise.
So if you remember how it worked
if the random number was less than no 0.5
then this function would resolve.
In other words, succeed.
The Promise would succeed.
Otherwise the Promise would reject.
So it was a kind of artificial example
a Promise that will either resolve successfully or reject.
And that Promise object gets returned back to the caller.
Well, here is the caller code.
And what I've done in my caller code
is I've used the async and the await keywords
to simplify how I handle the Promise object that comes back.
So my due task function returns the Promise.
Here's my user interface code.
There was a button click.
When the button gets clicked to kick off a new task
we call the due task function.
This function up here.
We give it a Promise count
and a random number between zero and one.
This function returns immediately with a Promise object.
And what I do then is I use the await keyword
which basically takes the rest of this function here
and puts it into a then callback.
So it won't proceed.
This function won't proceed
until that due task Promise has resolved.
Assuming it's resolved.
Once it's resolved the await keyword
will automatically grab the result
the correct result and assign it into here.
Normally that would be the value that you get
in your then callback function.
So this is basically a then callback
but without the syntax.
Wait for that function to resolve, grab the result
and then assign it to that variable there.
And then display the result on the screen.
The catch handler, there's a tri catch block here.
If you call a function that returns a Promise
but the Promise eventually rejects
then it comes into the catch handler.
That's what happens when you await the Promise
and the Promise is rejected
then it comes into a catch handler.
And this is the error message that would've been rejected.
So up here, when you call reject whatever value you reject
with the error message
that's the value that you catch on here.
And then we display that on the screen.
So in our user interface code we can use the await keyword
to wait for the Promise to be resolved
or to catch the exception if it isn't.
When you use the await keyword here
you have to prefix the function with async.
If you didn't you'd get an error,
you can't use the await keyword here,
unless you've already told JavaScript
that this is an asynchronous function.
So we can use async and await in our code.
We can also use those keywords in our tests.
So I've got a test with two functions.
A test function that will ensure that a task resolves.
If I give it a value less than an 0.5.
So similar kind of idea.
I call my two due task function
and I give it a value less than an 0.5.
So it should resolve as opposed to reject.
Await resolution.
So basically this code here gets moved into a then block.
It waits for that function to,
for that Promise to be resolved.
Assuming that it is resolved successfully,
it'll grab the result and assign it to that variable.
And then I can just check that's the result of the resolve.
I can check that the result of the Promise
is the correct string.
And don't forget that if you use the await keyword
in a function or a Lambda
you have to indicate that this function or Lambda is async.
If you have await here, you have to have async
before the function.
And the next example's similar
but it's checking for failure.
So I'm gonna pass in the value
equal to or greater than 0.5.
So when I call the due task function
this value here should, according to the logic in there
that should cause the Promise to reject.
So I call it await in the chance that it might resolve.
It doesn't resolve, it rejects.
And therefore, cause the catch handler.
And this error message here will be the error
that this then rejected with the error screen.
So we can just check that the error screen
is the correct error.
And then just one reminder
expect .assertions(1) is to ensure
that we don't get a false positive.
Just to remind you what could happen
if we weren't careful enough.
Imagining that my code here in due task was buggy.
And instead of rejecting when it should do
it actually resolved.
In which case it wouldn't go into the catch handler.
So it wouldn't execute this expect
and that wouldn't be right.
It should have done that.
So when you say expect one assertion
what we're saying is there must be one expect executed.
It must go into the catch handler otherwise it'll fail.
Because it should do.
It should go into the catch handler
and that's how we can verify that it did.
One last thing to show you is you can also use async08
in conjunction with the results and rejects matches.
Have a look at this code here.
This is a different test suite.
I've got a task which resolves correctly
if the value is less than not 0.5.
So if you remember the resolves matcher
expect call the function.
That function returns a Promise.
Verify that the Promise resolves
and that the result of that Promise is this.
So the await keyword here,
well, what actually happens is from there to there
is a Promise object.
So we basically say await on that Promise object to finish
before our test function returns
to prevent the test function from returning too early.
And similarly here call the due task function
with the value that we expect to be rejected.
So that returns to Promise.
Verified that the Promise was rejected
and that the rejection error string was that.
And again, from there to there is a Promise.
So we await completion of that Promise
before the test function returns back to test.
Okay, so we should run those tests.
I can just run all tests in this demo.
So in this demo I have all the tests we've just mentioned.
A test where we use the async and await keyword
first of all.
And in another test suite where we use the resolves
and the rejects matchers.
So I have two test suites covering the four tests altogether
that we've just gone through.
I can run all of those tests to verify
that they all give me the correct result.
So I can do that like so jest.
So it'll run both test suites.
One test suite that basically checked
the await async keywords on their own.
And then the second test suite that checked the rejects
and resolves matchers.
Now, because there's an element of a time of delay going on
it did take a little while to complete, but there you go.
So we have successfully used and checked
for the async and await using Promises.
And then we've used the resolves and rejects
matchers as well in conjunction with the async
and the await keywords.
So generally when you work with Promises
I would recommend using async and await.
It's easier than having then callbacks and cap callbacks.
You have a more linear flow through your code.
Makes it easier to write.