I'm building a website that reads data from a backend. That data is computed on-the-fly and sent back to the client in a buffered manner. I.e. as soon as the first chunk is computed it is sent to the client, then it computes the next chunk and sends that to the client. This whole process happens in the same HTTP request. The client should not wait for the complete response to finish but handle each chunk by its own as soon as has been sent. Such responses can usually be consumed using the XHR progress handler (e.g. How to get progress from XMLHttpRequest).
How can I consume such a response with the HttpModule in Angular2 using RxJS and Observables?
Edit: peeskillet gave an excellent and detailed answer below. In addition, I did some further digging and found a feature request for the HttpModule
of Angular and a StackOverflow question with another approach on how to solve it.
Note: The following answer is only a POC. It is meant to educate on the architecture of Http, and also provide a simple working POC implementation. One should take a look at the source for
XHRConnection
for ideas on what else you should consider when implementing this.When trying to implement this, I don't see any way to tap directly into the XHR. It seems maybe we need to just provide custom implementations of some of the components involved with using
Http
. The three main components that we should consider areConnection
ConnectionBackend
Http
Http
takes aConnectionBackend
as a argument to its constructor. When a request is made, say withget
,Http
creates a connection withConnectionBackend.createConnection
, and returns theObservable
property ofConnection
(that's returned fromcreateConnection
). In the most stripped down (simplified) view, it looks like thisSo knowing this architecture, we can try to implement something similar.
For the
Connection
, here is the POC. Imports left out for brevity, but for the most part, everything can be imported from@angular/http
, and theObservable/Observer
can be imported fromrxjs/{Type}
.Here's we are just subscribing to the XHR
progress
event. Since theXHR.responseText
spews out the entire concatenated text, we justsubstring
to get chunks, and emit each chuck through theObserver
.For the
XHRBackend
, we have the following (nothing spectacular). Again, everything can be imported from@angular/http
;For
Http
, we will extend it, adding agetChunks
method. You can add more methods if you want.The
mergeOptions
method can be found in theHttp
source.Now we can create a module for it. Users should directly use
ChunkedHttp
instead ofHttp
. But because to don't attempt to override theHttp
token, you can still useHttp
if you need to.We import the
HttpModule
because it provides other services that we need to be injected, but we don't want to have to reimplement those if we don't need to.To test just import the
ChunkedHttpModule
into theAppModule
. Also to test I used the following componentI have a backend endpoint set up where it just spits out
"Message #x"
in 10 chunks every half a second. And this is the resultThere seems to be a bug somewhere. There's only nine :-). I think it's server side related.