When I first started learning RxJS, I could instinctively see that observable streams offered all kinds of possibilities in solving many of the problems I encountered day to day in front end web application development. I’d already been using the flux architecture for a while, and had been blown away by the clarity of organisational structure and separation of concerns it brought to my web apps. I’d read that RxJS could do the same, and was keen to learn about how. The elegant handling of HTTP requests seemed like the obvious starting point for this learning journey. However, I quickly became frustrated at how little information I could find in one place regarding good practices around this topic (error handling in particular), and had to find out most things piecemeal though reading around, a lot of browsing Stackoverflow and Github issue threads, and personal experimentation. This article serves as a catalogue for what I have learned so far. I’m going to explain some handy ways of doing the following: - Creating observable streams from HTTP requests - Handling HTTP error response - The elegant handling of out of order HTTP request completion - Throttling user input - Some bonus extra tips an tricks I’m going to assume some knowledge of the absolute basics of creating and subscribing to observables, as this is easy to , and is where I was at when I started experimenting with RxJS and HTTP requests. find online The Example App To demonstrate all of these techniques we’ll create a example mini app that consumes g . It will enable the user to type a github username in a box and if valid, display their avatar underneath. I’ll use many variations of the app to demonstrate the different ways of using RxJS. To keep things simple the app only uses RxJS, bootstrap and a little bit of jQuery to glue the page together. ithub user api Note: the github user api, has a rather pathetic rate limit of 60 requests per hour if used unauthenticated. So if you get too trigger happy with the examples, they might stop working for a bit. The Setup To kick us off, let’s create a text input, and create an observable from its ‘keyup’ event. <div = > <h1>Search Github Users</h1> <div class="form-group"> <label for="search-box"> Username: </label> <input id="search-box" type="text" class="form-control" /> </div> <button id="search-button" class="btn btn-primary" > Search </button> </div> class "container" <!-- Search controls --> userClicksSearchButton = Rx.Observable.fromEvent( $( ), ) .map( { $( ).val(); }); userClicksSearchButton.subscribe( { alert(searchTerm); }); let "#search-button" "click" ( ) => event return "#search-box" ( ) => searchTerm Example 1: Input Box View the live example in codepen Type in the box and hit the search button to see an alert containing the text input value. Notice the extra chained after the observable. This enables us to map the current input value of search input box to our observable, replacing the default event object that would normally be emitted. map fromEvent userClicksSearchButton Handling an HTTP request OK, we now have an observable we can work with that will emit the user’s input when they hit the search button. To fire off our HTTP request, we’re going to create an observable stream we can subscribe to using our userClicksSearchButton as a source: <div = > <h1>Search Github Users</h1> <div class="form-group"> <label for="search-box"> Username: </label> <input id="search-box" type="text" class="form-control" /> </div> <button id="search-button" class="btn btn-primary" > Search </button> <hr /> <!-- Search result --> <a href="" target="_blank" id="search-result" style="display:none;" > <h2 id="search-result__login"></h2> <img src="" alt="avatar" width="150px" height="150px" id="search-result__avatar" /> </a> </div> class "container" <!-- Search controls --> userClicksSearchButton = Rx.Observable.fromEvent( $( ), ) .map( { $( ).val(); }); userClicksSearchButton .flatMap( { Rx.Observable.fromPromise( $.get( + searchTerm) ); }) .subscribe( { renderUser( response.login, response.html_url, response.avatar_url ); }); { searchResult = $( ); searchResult.show(); searchResult.attr( , href); $( ).attr( , imgSrc); $( ).text(login); } let "#search-button" 'click' ( ) => event return "#search-box" ( ) => searchTerm return 'https://api.github.com/users/' ( ) => response ( ) function renderUser login, href, imgSrc let "#search-result" "href" "#search-result__avatar" 'src' '#search-result__login' Example 2: Button Search View the live example in codepen Have a go at typing your Github username into the box and hitting search, you should see your avatar appear underneath. Be aware that currently, if you search for an invalid user name, it will break the app. Don’t worry about this right now; we’ll sort that out in a bit. We have chained after our first observable, subscribed to the resulting observable, and written some of the received data to the DOM. flatMap We have carried out the request using a standard jQuery get, which we have wrapped in RxJS’ helpful to turn it into an observable. fromPromise FlatMap Take a look at the use of `flatMap` in the example. I must admit, when I first looked at a few examples of this kind of thing, it confused the hell out of me. At first glance it looks like we should just be able to call like we did for the click event, after all, we’ve already converted the promise into an observable by calling right? map fromPromise In actual fact, what returns is an observable stream , not a stream of the objects that the promise would emit when it resolves. allows us to flatten all of those promise resolutions into a single observable stream, and when we subscribe to it, we get just the response object that jQuery would originally emitted on . fromPromise of promises flatMap then() Handling Errors Earlier I mentioned that the app currently breaks if you search for a invalid user name (try searching for some gibberish followed by something perfectly valid). This obviously sucks, and initially, it is not at all obvious why this is happening. To understand why, let’s look at the order of events that takes place as the promise resolves: 1. HTTP request completes, and jQuery rejects the promise (because it’s a 404) 2. The observable created by throws an error, as this is how it reacts to a rejected promise fromPromise 3. The error goes uncaught, and hence gets thrown again in the parent observable flatMap 4. The observable will no longer emit because after an observable stream has thrown an error, it is terminated. flatMap This has the unintended side effect of making our search button useless every time we get an error response. This isn’t very good, so how can we deal with this problem? Here’s the app again, but this time with error handling: <div = > <h1>Search Github Users</h1> <div class="form-group"> <label for="search-box"> Username: </label> <input id="search-box" type="text" class="form-control" /> </div> <button id="search-button" class="btn btn-primary" > Search </button> <hr /> <!-- Search result --> <a href="" target="_blank" id="search-result" style="display:none;" > <h2 id="search-result__login"></h2> <img src="" alt="avatar" width="150px" height="150px" id="search-result__avatar" /> </a> <!-- Error message --> <div id="error" class="well" style="display:none;" > <p id="error__message"> </p> </div> </div> class "container" <!-- Search controls --> userClicksSearchButton = Rx.Observable.fromEvent( $( ), ) .map( { $( ).val(); }); userClicksSearchButton .flatMap( { Rx.Observable.fromPromise( $.get( + searchTerm) ) .catch( { renderError(response.statusText); Rx.Observable.empty(); }); }) .subscribe( { renderUser( response.login, response.html_url, response.avatar_url ); }); { $( ).show(); $( ).hide(); $( ).attr( , href); $( ).attr( , imgSrc); $( ).text(login); } { $( ).hide(); $( ).show(); $( ).text(message); } let "#search-button" 'click' ( ) => event return "#search-box" ( ) => searchTerm return 'https://api.github.com/users/' ( ) => response return ( ) => response ( ) function renderUser login, href, imgSrc "#search-result" "#error" "#search-result" "href" "#search-result__avatar" 'src' '#search-result__login' ( ) function renderError message "#search-result" "#error" "#error__message" Example 3: Button Search With Error Handling View the live example in codepen Now, we catch and handle the error before it travels upstream, and replace the observable with an empty completed one that will get flattened in it’s place. For this we use the handy . Rx.Observable.empty() Handling out of order requests Let’s alter the example a bit. Suppose instead of clicking a button to search, we want the user to be able to type into the box, and have their search be carried out as they type. For this we will need to replace the userClicksSearchButton observable with a userTypesInSearchBox observable like so: <div = > <h1>Search Github Users</h1> <div class="form-group"> <label for="search-box"> Username: </label> <input id="search-box" type="text" class="form-control" /> </div> <hr /> <!-- Search result --> <a href="" target="_blank" id="search-result" style="display:none;" > <h2 id="search-result__login"></h2> <img src="" alt="avatar" width="150px" height="150px" id="search-result__avatar" /> </a> </div> class "container" <!-- Search controls --> userTypesInSearchBox = Rx.Observable.fromEvent( $( ), ) .map( { $( ).val(); }); userTypesInSearchBox .flatMap( { Rx.Observable.fromPromise( $.get( + searchTerm) ).catch( Rx.Observable.empty()); }) .subscribe( { renderUser( response.login, response.html_url, response.avatar_url ); }); { searchResult = $( ); searchResult.show(); searchResult.attr( , href); $( ).attr( , imgSrc); $( ).text(login); } let "#search-box" 'keyup' ( ) => event return "#search-box" ( ) => searchTerm return 'https://api.github.com/users/' => () ( ) => response ( ) function renderUser login, href, imgSrc let "#search-result" "href" "#search-result__avatar" 'src' '#search-result__login' Example 4: Text Search View the live example in codepen We now find that because each of our requests are fired in quick succession, we can no longer guarantee that they will complete in the order they were initiated. This could cause our searches to result in a non-matching avatar being displayed underneath. This isn’t great, so to solve this problem, we will use concatMap. ConcatMap is a lot like except it will preserve the order of the source emissions, even if the observable it is flattening emits in a different order. For example, if I search for ‘Elle’, and immediately afterwards for ‘Ellen’, and the ‘Ellen’ request happens to complete first, will wait until the ‘Elle’ request has completed, before emitting both results in immediate succession in the order ‘Elle’ ‘Ellen’. concatMap flatMap concatMap Here’s the code, amended to use ‘concatMap’. <div = > <h1>Search Github Users</h1> <div class="form-group"> <label for="search-box"> Username: </label> <input id="search-box" type="text" class="form-control" /> </div> <hr /> <!-- Search result --> <a href="" target="_blank" id="search-result" style="display:none;" > <h2 id="search-result__login"></h2> <img src="" alt="avatar" width="150px" height="150px" id="search-result__avatar" /> </a> <!-- Error message --> <div id="error" class="well" style="display:none;" > <p id="error__message"> </p> </div> </div> class "container" <!-- Search controls --> userTypesInSearchBox = Rx.Observable.fromEvent( $( ), ) .map( { $( ).val(); }); userTypesInSearchBox .concatMap( { Rx.Observable.fromPromise( $.get( + searchTerm) ) .catch( Rx.Observable.empty()); }) .subscribe( { renderUser( response.login, response.html_url, response.avatar_url ); }); { $( ).show(); $( ).attr( , href); $( ).attr( , imgSrc); $( ).text(login); } let "#search-box" 'keyup' ( ) => event return "#search-box" ( ) => searchTerm return 'https://api.github.com/users/' => () ( ) => response ( ) function renderUser login, href, imgSrc "#search-result" "#search-result" "href" "#search-result__avatar" 'src' '#search-result__login' Example 5: Text Search Preserving Request Order View the live example in codepen I’ve just learned that we could also use for this. This provides the added advantage of cancelling the underlying redundant http request if necessary. Edit: switchMap Throttling user input At the moment, our app fires a request every single time the user types a letter into the box. This seems like overkill, given that the user isn’t really interested in seeing a result until they’ve typed at least several letters into the box. Why hammer the server when it add nothing to the user experience? Using RxJS, we can ease this problem with a simple extra function call to ‘debounce’. <div = > <h1>Search Github Users</h1> <div class="form-group"> <label for="search-box"> Username: </label> <input id="search-box" type="text" class="form-control" /> </div> <hr /> <!-- Search result --> <a href="" target="_blank" id="search-result" style="display:none;" > <h2 id="search-result__login"></h2> <img src="" alt="avatar" width="150px" height="150px" id="search-result__avatar" /> </a> </div> class "container" <!-- Search controls --> userTypesInSearchBox = Rx.Observable.fromEvent( $( ), ) .map( { $( ).val(); }); userTypesInSearchBox .debounce( ) .concatMap( { Rx.Observable.fromPromise( $.get( + searchTerm) ) .catch( Rx.Observable.empty()); }) .subscribe( { renderUser( result.login, result.html_url, result.avatar_url, result.searchTerm ); }); { $( ).show(); $( ).attr( , href); $( ).attr( , imgSrc); $( ).text(login); } let "#search-box" 'keyup' ( ) => event return "#search-box" 250 ( ) => searchTerm return 'https://api.github.com/users/' => () ( ) => result ( ) function renderUser login, href, imgSrc "#search-result" "#search-result" "href" "#search-result__avatar" 'src' '#search-result__login' Example 6: Text Search Throttled View the live example in codepen In this example, it’s worth looking in the network tab of the console window as you type in the box to see that many fewer requests are sent than in the previous example. Debounce ‘Debounce’ accepts a number parameter that represents the number of milliseconds the observable should wait after the previous emission before emitting again. After the time has elapsed, the observable will emit the last emission from within the time period only, ignoring any others. I’ve found this to be very helpful for live text-based search features. Note: if you need it to emit the opposite, i.e. the first emission from within the time period, you can use the method instead. throttle Extra tips and tricks The above techniques will handle most HTTP request use cases, but there are some other cool things you can do to ease some of the pain that comes from asynchronous request handling: Take Sometimes, you’re only interested in the first 1, 2 or n user interactions. For these situations RxJS provides the ‘take’ method. In our search button example, lets say we wanted to only show the first valid result: <div = > <h1>Search Github Users</h1> <div class="form-group"> <label for="search-box"> Username: </label> <input id="search-box" type="text" class="form-control" /> </div> <hr /> <!-- Search result --> <a href="" target="_blank" id="search-result" style="display:none;" > <h2 id="search-result__login"></h2> <img src="" alt="avatar" width="150px" height="150px" id="search-result__avatar" /> </a> </div> class "container" <!-- Search controls --> userTypesInSearchBox = Rx.Observable.fromEvent( $( ), ) .map( { $( ).val(); }); userTypesInSearchBox .debounce( ) .concatMap( { Rx.Observable.fromPromise( $.get( + searchTerm) ) .catch( Rx.Observable.empty()); }) .take( ) .subscribe( { renderUser( response.login, response.html_url, response.avatar_url ); }); { $( ).show(); $( ).attr( , href); $( ).attr( , imgSrc); $( ).text(login); } let "#search-box" 'keyup' ( ) => event return "#search-box" 250 ( ) => searchTerm return 'https://api.github.com/users/' => () 1 ( ) => response ( ) function renderUser login, href, imgSrc "#search-result" "#search-result" "href" "#search-result__avatar" 'src' '#search-result__login' Example 7: Text Search With Take View the live example in codepen Now, the first search performed will work, but anything subsequently typed into the box will be ignored. Filter Sometimes we want to filter out certain observable emissions based on a condition. To demonstrate, let’s use to make our text search example ignore searches until they are at least 5 characters long: filter <div = > <h1>Search Github Users</h1> <div class="form-group"> <label for="search-box"> Username: </label> <input id="search-box" type="text" class="form-control" /> </div> <hr /> <!-- Search result --> <a href="" target="_blank" id="search-result" style="display:none;" > <h2 id="search-result__login"></h2> <img src="" alt="avatar" width="150px" height="150px" id="search-result__avatar" /> </a> </div> class "container" <!-- Search controls --> userTypesInSearchBox = Rx.Observable.fromEvent( $( ), ) .map( { $( ).val(); }); userTypesInSearchBox .debounce( ) .filter( { searchTerm.length >= ; }) .concatMap( { Rx.Observable.fromPromise( $.get( + searchTerm) ) .catch( Rx.Observable.empty()); }) .subscribe( { renderUser( response.login, response.html_url, response.avatar_url ); }); { $( ).show(); $( ).attr( , href); $( ).attr( , imgSrc); $( ).text(login); } let "#search-box" 'keyup' ( ) => event return "#search-box" 250 ( ) => searchTerm return 5 ( ) => searchTerm return 'https://api.github.com/users/' ( ) => response ( ) => response ( ) function renderUser login, href, imgSrc "#search-result" "#search-result" "href" "#search-result__avatar" 'src' '#search-result__login' Example 8: Text Search Filtered View the live example in codepen Merge Sometimes, it is useful to merge more than one observable into a single observable. To demonstrate, let’s use to give our search app two inputs which you can search by typing into either one: merge <div = > <h1>Search Github Users</h1> <div class="form-group"> <label for="search-box-1"> Username: </label> <input id="search-box-1" type="text" class="form-control" /> </div> <div> <label for="search-box-2"> Username: </label> <input id="search-box-2" type="text" class="form-control" /> </div> <hr /> <!-- Search result --> <a href="" target="_blank" id="search-result" style="display:none;" > <h2 id="search-result__login"></h2> <img src="" alt="avatar" width="150px" height="150px" id="search-result__avatar" /> </a> </div> class "container" <!-- Search controls --> userTypesInSearchBox1 = Rx.Observable.fromEvent( $( ), ) .map( { $( ).val(); }); userTypesInSearchBox2 = Rx.Observable.fromEvent( $( ), ) .map( { $( ).val(); }); userTypesInSearchBox1 .merge(userTypesInSearchBox2) .debounce( ) .concatMap( { Rx.Observable.fromPromise( $.get( + searchTerm) ) .catch( Rx.Observable.empty()); }) .subscribe( { renderUser( response.login, response.html_url, response.avatar_url ); }); { $( ).show(); $( ).attr( , href); $( ).attr( , imgSrc); $( ).text(login); } let "#search-box-1" 'keyup' ( ) => event return "#search-box-1" let "#search-box-2" 'keyup' ( ) => event return "#search-box-2" 250 ( ) => searchTerm return 'https://api.github.com/users/' => () ( ) => response ( ) function renderUser login, href, imgSrc "#search-result" "#search-result" "href" "#search-result__avatar" 'src' '#search-result__login' Example 9: Text Search With Merge View the live example in codepen Keeping data for later A potential issue with all of the examples above is that when we are in the latter stages of the observable stream, after the request has completed, we’ve lost the reference to the original payload (in this case, our search term text). We can get around this bundling the original payload in with the response payload while we still have access to it via lexical scoping. For example, let’s say we wanted to write the search term to the page again as a part of the function: render <div = > <h1>Search Github Users</h1> <div class="form-group"> <label for="search-box"> Username: </label> <input id="search-box" type="text" class="form-control" /> </div> <hr /> <!-- Search Term --> <div> <span> <strong>Search Term:</strong> </span> <span id="search-term-text"></span> </div> <!-- Search result --> <a href="" target="_blank" id="search-result" style="display:none;" > <h2 id="search-result__login"></h2> <img src="" alt="avatar" width="150px" height="150px" id="search-result__avatar" /> </a> </div> class "container" <!-- Search controls --> userTypesInSearchBox = Rx.Observable.fromEvent( $( ), ) .map( { $( ).val(); }); userTypesInSearchBox .debounce( ) .concatMap( { Rx.Observable.fromPromise( $.get( + searchTerm) ) .map( { { : response, : searchTerm } }) .catch( Rx.Observable.empty()); }) .subscribe( { renderUser( result.response.login, result.response.html_url, result.response.avatar_url, result.searchTerm ); }); { $( ).show(); $( ).attr( , href); $( ).attr( , imgSrc); $( ).text(login); $( ).text(searchTerm); } let "#search-box" 'keyup' ( ) => event return "#search-box" 250 ( ) => searchTerm return 'https://api.github.com/users/' ( ) => response return response searchTerm => () ( ) => result ( ) function renderUser login, href, imgSrc, searchTerm "#search-result" "#search-result" "href" "#search-result__avatar" 'src' '#search-result__login' '#search-term-text' Example 10: Text Search With Retained Data View the live example in codepen Combine latest One of the best features of RxJS is how easy it is to combine multiple streams into one. To demonstrate, let’s adapt our example to carry out two searches and allow the user to compare the resulting avatars next to each other, but only after both searches have a result: <div = > <h1>Compare Github Users</h1> <div class="row"> <div class="col-xs-6"> <div class="form-group"> <label for="search-box"> Username 1: </label> <input id="search-box-1" type="text" class="form-control" /> </div> </div> <div class="col-xs-6"> <div class="form-group"> <label for="search-box"> Username 2: </label> <input id="search-box-2" type="text" class="form-control" /> </div> </div> </div> <hr /> <div class="row"> <div class="col-xs-6"> <!-- Search result 1 --> <a href="" target="_blank" id="search-result-1" style="display:none;" > <h2 id="search-result-1__login"></h2> <img src="" alt="avatar" width="150px" height="150px" id="search-result-1__avatar" /> </a> </div> <div class="col-xs-6"> <!-- Search result 2 --> <a href="" target="_blank" id="search-result-2" style="display:none;" > <h2 id="search-result-2__login"></h2> <img src="" alt="avatar" width="150px" height="150px" id="search-result-2__avatar" /> </a> </div> </div> </div> class "container" <!-- Search controls --> userTypesInSearchBox1 = Rx.Observable.fromEvent( $( ), ) .map( { $( ).val(); }); userTypesInSearchBox2 = Rx.Observable.fromEvent( $( ), ) .map( { $( ).val(); }); searchResult1 = userTypesInSearchBox1 .debounce( ) .concatMap( { Rx.Observable.fromPromise( $.get( + searchTerm) ) .catch( { Rx.Observable.empty(); }); }); searchResult2 = userTypesInSearchBox2 .debounce( ) .concatMap( { Rx.Observable.fromPromise( $.get( + searchTerm) ) .catch( { Rx.Observable.empty(); }); }); Rx.Observable .combineLatest(searchResult1, searchResult2) .subscribe( { renderUsers( results[ ].login, results[ ].html_url, results[ ].avatar_url, results[ ].login, results[ ].html_url, results[ ].avatar_url ); }); { $( ).show(); $( ).attr( , href1); $( ).attr( , imgSrc1); $( ).text(login1); $( ).show(); $( ).attr( , href2); $( ).attr( , imgSrc2); $( ).text(login2); } let "#search-box-1" 'keyup' ( ) => event return "#search-box-1" let "#search-box-2" 'keyup' ( ) => event return "#search-box-2" let 250 ( ) => searchTerm return 'https://api.github.com/users/' ( ) => response return let 250 ( ) => searchTerm return 'https://api.github.com/users/' ( ) => response return ( ) => results 0 0 0 1 1 1 ( ) function renderUsers login1, href1, imgSrc1, login2, href2, imgSrc2 "#search-result-1" "#search-result-1" "href" "#search-result-1__avatar" 'src' '#search-result-1__login' "#search-result-2" "#search-result-2" "href" "#search-result-2__avatar" 'src' '#search-result-2__login' Example 11: Text Search With Combine Latest View the live example in codepen What next? There are many ways to get the most out of the power of RxJS to handle HTTP requests, and we’ve touched upon a few of these. However, the RxJS API is a complex beast and we’ve only scratched the surface on the many different ways RxJS methods can do cool stuff with HTTP streams. Once you’ve got your head round a few, it’s well worth checking out the to experiment with a few more that might help out in more specific use cases docs