Using RxJS Observables With JavaScript Async and Await
Time to read: 7 minutes
There are a number of resources for handling asynchronous tasks in JavaScript, each with its own strengths and suitability to specific tasks. Sometimes a single tool is all that’s necessary to accomplish a task, but there are programming challenges which can be handled more effectively by combining the capabilities of tools.
RxJS Observables enable you to operate on sequences of data, like the results of a REST API call, as if they were events, acting on new and updated data from the Observable object when the change occurs. They’re great tools to use when the timing of data events is unpredictable, like when you’re dealing with slow or occasionally unreliable web services.
JavaScript Promises are useful when you need a placeholder for the results of a task that might fail and need to respond differently depending on the task’s success or failure. Promise objects can be used with the JavaScript async and await keywords to hold the processing of a program’s main path of execution until a Promise is resolved. That’s great when your user interface behavior depends on the results of an asynchronous action.
So what do you do when your program needs to wait for the results of a slow, less-than-completely-reliable web service to determine what to do next?
You can use Observables with Promises and with async/await to benefit from the strengths of each of those tools. This post will show you how to code the combination of Observables, Promises, and async/await so your program can react to the state of an Observable and the resolution of a Promise, including errors, with timely user interface information.
Prerequisites
You’ll need the following resources to build and run the code presented in this post:
Node.js and npm – The Node.js installation will also install npm.
Visual Studio Code – or another IDE or editor
Git – for source code control or cloning the companion repository
Mocklets account – If you want to experiment with API mocking, create your own account. This tutorial’s code includes a link to a mock API set up by the author, so you don’t need an account to run the code. Free tier accounts are run-rate limited, so the one provided with this post might be too busy to respond to your GET requests if this post is popular on any given day.
Twilio account – Although not required for this tutorial, if you sign up for a free Twilio trial account with this link you will receive an additional $10 credit when you convert to a regular account.
In addition to these tools, you should also have a working knowledge of JavaScript basics and some exposure to JavaScript Promises, the async
and await
keywords, and RxJS Observables.
There is a companion repository containing the complete source code for this project on GitHub.
Setting up the Node.js project
Open a console window in the directory where you’d like to locate the project directory, then use the following command-line instructions to create the project directory, initialize a Git repo, and install the necessary npm modules:
You may see a number of npm WARN
messages, but they’re nothing to get hung about. 🍓
Coding the program
The demonstration program consists of an asynchronous function, getAndRetry
and an anonymous asynchronous function that calls it. It only takes a few lines of code to show the power of this technique.
In the retry directory, create a new file named index.js and insert the following code:
That’s it!
Testing the completed app
It will be easier to understand what the code is doing if you see it in action, so run the program by executing the following command-line instruction in the retry directory:
Then run it again. And again. And even more times if you’re having fun.
The code will try up to 11 times to get a successful response from a mock REST API. If it encounters an error it will log the error and retry, up to the retry limit of 10; the first attempt isn’t a retry. If a success response is received the body of the response will be logged to the console and the program will stop.
You might notice that some messages take longer than others to appear. A delay of three seconds is built into the 503 response.
The mock API will randomly return three responses:
Status: 200 OK
Content-Type: text/plain
Body: Congratulations! Everything's coming up 200 with this API.
Status: 400 Bad Request
Content-Type: application/json
Body: {}
Status: 503 Service Unavailable
Content-Type: application/json
Body: {}
Because the API is designed to be wobbly, your results will vary. In six trial runs it produced the following:
If all the retries are exhausted the program will let you know and quit. To see that more easily, change the 10
in the following line of code in the anonymous function to a 1
:
It may take a few tries, but you’ll eventually see output like this:
There are a few points of interest in how this output is produced:
- The success, error, and exhaustion messages come from different places in the program.
- The
await
-ed call to theasync
functiongetAndRetry
finishes before execution resumes in the anonymous function. - The only output that isn’t produced by an error handler is the logging of a successful response.
Understanding the code
Once you’ve gotten a sense for how the program behaves you can dig into the specifics of the code and get a better understanding of how the asynchronous technologies work.
The top level of execution in this demonstration app is the anonymous function marked with the async
keyword. It calls the getAndRetry
function and awaits its results before proceeding. When the getAndRetry
function successfully returns a value the body of the HTTP response is sent to the console.
Because the function is wrapped in a try…catch
structure, errors thrown by getAndRetry
will be caught in the catch
block. In the narrow context of this demonstration, the error that will be thrown by the getAndRetry
function will be when the Promise returned by the getAndRetry
function is rejected.
To understand why, take a look at the getAndRetry
function. The function uses the RxHR library to create an Observable from the response received from the target address specified by the url
parameter using its .get
method.
Because RxHR creates an RxJS Observable, you can use RxJS operators with it. Operators are functions. There are two kinds: 1) those that take Observables as input and return Observables as output and 2) those that are used to create a new observable.
The pipe function takes the original Observable and uses it in all the operators within the scope of the .pipe
. Within the .pipe
the RxJS tap operator is used to perform a side action that doesn’t affect the Observable’s state or contents. The term function in RxJS refers to functions which produce output.
The body of the tap
operator is used to check the HTTP status code returned from the endpoint at the url
address. If it’s equal to, or greater than, 400 the tap
operator throws an error.
The error thrown by the tap
operator is caught by the RxJS catchError operator, which sends the url
value and the error
to the console. The form of the error is the url
tried and the output.response.statusCode
received.
The catchError
operator can be used to return a new Observable when an error is thrown or continue with the existing Observable. In getAndRetry
the operator continues with the original Observable, the one that calls the address in url
.
The RxJS retry operator returns the original Observable minus the error until the number of attempts specified in the retryCount
parameter is exceeded. If there hasn’t been a successful attempt at that point the error is returned by the Observable.
Of course, if the value of output.response.statusCode
is less than 400 the response from the url
address is emitted by the Observable. At that point the RxJS Observable toPromise function is used to convert the RxJS Observable to a JavaScript Promise.
If the Promise is fulfilled, synchronous processing resumes and the contents of results.body
are sent to the console. Using the Mocklets API provided in this post and the companion repository, the response will be:
If the response from getAndRetry
is a rejected Promise, that’s caught as an error and the console receives a disappointed message:
Either of those outcomes concludes program execution.
Further exploration
Here are a few suggestions for ways you can use this demonstration application to better understand asynchronous JavaScript and how RxJS Observables can be used with async
and await
:
- Set breakpoint in Visual Studio Code, or another capable IDE, to examine the state of the Observables and the Promise as the code executes.
- Add other functions that create RxJS Observables and return Promises.
- Create a structure where a Promise is either fulfilled or rejected depending on the condition of multiple Observables.
- Add structures that follow the sample code provided in the RxJS and RxHR documentation.
- Create your own Mocklets API to simulate real-world REST API interaction.
- Sign up for a free Twilio trial account and use this program to check the Twilio Status API.
If you dig into the RxJS and RxHR documentation you’ll find there are a number of ways to write functions that use those technologies. You can experiment with those as well.
Summary
In this post you built a Node.js application to demonstrate how to use RxJS Observables with JavaScript Promises and the async
and await
keywords. The program you built demonstrated how Mocklets can be used to create dynamic REST API simulations for application development and testing. You saw how to use a number of RxJS operators and how to convert an RxJS Observable to a JavaScript Promise.
Additional resources
There are a number of posts on the Twilio blog which provide more information about asynchronous JavaScript, including RxJS Observables and Promises:
Asynchronous JavaScript: Understanding Callbacks
Asynchronous JavaScript: Introduction to JavaScript Promises
Asynchronous JavaScript: Advanced Promises with Node.js
Asynchronous JavaScript: Introducing ReactiveX and RxJS Observables
Asynchronous JavaScript: Using RxJS Observables with REST APIs in Node.js
Asynchronous JavaScript: Introducing async and await
Asynchronous JavaScript: Using Promises With REST APIs in Node.js
If you’re new to asynchronous JavaScript, or JavaScript in general, you’ll get a comprehensive overview of the asynchronous tools by reading these posts in the order they’re listed.
You might also want to refer to the following canonical sources of documentation:
JavaScript – The MDN Web Docs are a good resource for learning to write JavaScript for browsers and Node.js.
RxJS – This is the documentation JavaScript implementation of ReactiveX. You might find it helpful in the event “Think of RxJS as Lodash for events.” doesn’t explain everything.
AJ Saulsberry is a technical editor at Twilio. Get in touch with him if you have been “there and back again” with a software development topic and want to make money writing about it for the Twilio blog.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.