How to write cancellable http requests with Angular $http service

Inspiration

The recent spread of the mobile internet connection almost completely changed the job profile of almost every frontend-developer. We steadily try to make our web pages responsive, and fearlessly fight against a wide variety of mobile browsers. But the mobile internet access has one more, a less freqently mentioned danger that causes sleepless nights for fronter developers.

Its speed is just incalculable.

Sometimes it works fine, but when it comes to bouncing along on the metro or just simply travelling in the counrtyside, it can become a disaster. On top of these, there is hardly any need to introduce the “always-running-out-of-broadband-data” syndrome. It might seem quite strange that I refer to this phenomenon as a complete disaster but slowness has some really strange side effects…

cancellable http requests angular clicking

…As if it were some ancient instinct, an overwhelming majority of people react to the extra slow internet connection with an extra big number of pointless clicking and tapping attempts. For a web-developer this means hundreds of untrackable asynchronous requests, and a massive allocation on the server. It was only a matter of time before one of our customers spoke up about this adverse situation and demanded a solution.

But what can a poor, confused junior developer offer in a sutiation like this?

  • The universal solution: ostrich policy. Let it go, and let them click anywhere. If you are a lucky fellow, you may be able to keep a lid on things. Yet it is quite easy to find yourself on the battlefield of chaotically fluttering http requests and customer complaints.
  • It would be very useful if we could disable all the buttons while performing a request. If someone does not want to wait endlessly, we still have the good old timeout parameter. But it is still a far too long to make the user wait.

All in all, we need to admit, that the essence of the problem are the unwanted, redundant requests. There is no use of handling  good-for-nothing responses if the user changes his/her mind since the request was launched. A question arises as to whether we can cancel an action by manually tapping/clicking on a button? Can we manage this while not giving up the indisputably useful automatic timeout functionality?  I think, this is the time for moving contemptation to action and make an attempt to find a solution for this problem.

Javascript promises as $http timeout parameter

When I am talking about Angular $http service and timeout parameter, everybody remembers the good old solution with a simple number in the parameter list of the http request:

But after some further investigation in the Angular documentation of $http service,  I stumbled into a very interesting sentece:

  • timeout – {number|Promise} – timeout in milliseconds, or promise that should abort the request when resolved.

Timeout can be a promise, too! And promises can be resolved, rejected, can also have automatic timeout. For a fleeting moment I felt the light bulb illuminating in my head. That time I didn’t know, that there was only worse to come.

It’s failure that gives you the proper perspective on success

Soon I decided to overwrite my http requests by using my newly acquired skills and use a javascript promise as an http timeout parameter. I bounded the cancellation of the promise to a click event of a button. Moreover, I set a countdown right after sending each http request by using Javascript the timeout method. After waiting a specified number of milliseconds, the promise became rejected automatically. The request in my service looked like this:

When it came to testing, my first impressions were encouragingly good. The http request cancellation worked like a charm! Unfortunately, all my expectations fell apart soon. The automatic timeout begun to behave unpredictably, cancelling the request in random moments. This behavior seemed to be just incalculabe. For hours and hours I hunted for the reasons, but everything seemed to work perfectly except for these random cancellations. It took me quite a while to see the reason of the bad behavior:

  • at the beginning of the first request a timeout period is launched. If the user cancels the request manually, or it just simply returns with a success, the countdown does not stop at all.
  • if the user makes other requests during this timeout period, it is possible that the earlier launched timeout expires during the request, and the “canceller.resolve()” expression will be evaluated, as the canceller variable contains the promise of the current request now.

Represeting these events on a timeline, the reason of the random cancellation becomes even clearer:

cancellable http requests angular promise handling

The aims are now clear: every request needs its own canceller function and a promise for a timeout parameter. Though the implementation is a bit tricky in Angular, in the next paragraph I will show you how I solved this issue by a simple example.

The yearbook

Let’s imagine that you are asked to make an online yearbook for your classmates, who are graduating from secondary school. The application contains two states. A main list state, containing the list of the students, and a profile state, that contains the detailed profile of a given student,.

cancellable http requests angular yearbook appcancellable http requests angular app details

Every state has a factory and a service. All the data is saved temporarly in javascript variables in factories. If a controller needs any data, it asks the factory for it. If it is not stored in the factory, the factory calls the proper function in the service, that sends the http request for the server.

The profile state is a child state of the main list state, and can be reached by clicking on a given student’s picture. The app does not navigate to the given profile till the data of the chosen student is not loaded. The aim is to be able to launch multiple pararell requests, and cancel the ones you don’t need any more. The following illustration summarized the aforsaid architecture:

cancellable http requests angular app architecture

The process we need to concentrate on is highlighted with red. If the user clicks on a picture in the list, the ListController asks the given student’s data from DetailsFactory. If the data is not already stored, the DetailsSercvice will send the http request.

And here comes th big trick. For every single request we need a new promise, and a  new canceller function. These three should belong together and be a member of a so-called RequestHandler function. In case of a new request we need to instantiate a new RequestHandler in the DetailsFactory and also in the DetailsService.

All we need to do is to perform the following changes to the code:

Combined this with some additional css tricks, our webapp will take its final shape:

cancellable http requests angular clicking solutions

Feel free to check it out in action in the following plunker:

https://plnkr.co/edit/7nqx9g?p=info

If you would like to test the request cancellation by a simulating slow Internet connection, follow the next instructions (Chrome):

  • open the plunker and wait till the main list is loaded
  • open the development tools  by pressing f12 
  • navigate to the Network tab
  • add a custom network throttling profile with a 1 kb/s download and upload rate, with 20000ms latency.

All in all

I hope I could provide some useful advice to fix the horrendeous clicking habits of your users. If you have any suggestions on this topic, I’d be glad to converse!

 

Csenge Sóti

Csenge Sóti

Web-Developer at Wanari Ltd.
“Life is a series of building, testing, changing and iterating.” -Lauren Mosenthal

  • I tried this, and when I go to the app in my browser it’s something I saw that “BrowserSync” Can anyone tell me what is the actual problem is?

    • Csenge Sóti

      Hi Jessica, thanks for your comment! I’m not exactly sure what your question is directed at! As far as I know Browsersync is a 3rd party library for testing. Could you please rephrase your question? 🙂