I’m not going to cover the integration between Servo and the Js engine, so you’re going to have to believe me that the call to fetch in the above Js code will result in the below Rust code being called.

Ok, so it looks like it’s a sender to this channel_manager, and let’s skip how the globalscope actually gets that sender in the first place, assuming it is done somewhere in the initialization of a “script-thread”.

Let’s follow the target.process_response_chunk call above. While the above code runs in the ‘main’ fetch thread, and the messages received on done_chan are coming from a request-dedicated ‘fetch worker’ thread, there is also a third thread involved in this dance: the “script thread”, where the event-loop of the webpage(or a worker) where this request was initiated is running.

Actually, a lot of those “threads” are actually separate processes, I am not particularly precise on this point. An ipc-channel is also used in the code.

So when you read “thread”, you might want to swap it mentally with “process”, or perhaps just settle for “concurrent component”(or is it parallel?)

As you can read, that call will end-up sending a message to another thread, containing a chunk of data. That ‘other thread’ is not yet the script-thread, it will be the router thread of the ipc-sender(I think, actually not sure there).

So how is this new chunk of data going to be handled by the script thread? That will be done through the so-call “networking task source”, which in Servo is basically implemented as a channel.

Ok, so we can sort of tell that a method called process_response_chunk will end up being called on a “listener” of sorts, by way of yet another Action trait that will call the process method of this message.

Let’s go back to the code that actually started this fetch in the first place:

Aha(yes, again)! We now see that the message, implementing Action , ends up inside a ListenerTask, and queued on the networking task source.

This is actually extremely interesting, because it shows how something, here a “fetch”, happening in one thread, will affect the world of another thread, the script-thread where you favorite web page is running, by having some task queued on it’s event-loop! Heureka, there is no shared state, only inter-thread messaging, with the handlers of such messages queuing “tasks” on the event-loop of the receiving thread.

So how does such a ‘task’ get executed on the target event-loop? Well, there is yet another trait for that, called TaskOnce. Let’s skip the trait definition, and look at how ListenerTask implements it:

Wow, I think I actually see a call to a process method of an action right there! Now go back to one of the gist a little bit above, what happens when the process method of a FetchResponseMsg is called? It simply matches on self, and depending on the kind of message it is, calls the appropriate method of a “listener”, implementing FetchResponseListener, passed to it. In this case its our context that is passed to it, and what is our context? It’s a FetchContext, which implements a process_response_chunk method, yep, there it is:

There we are, finally, a message was sent from the fetch thread, when it was handled by the ipc router, it resulted in a task being queued on the “networking task source” of the webpage event-loop, and when that task is finally run, it would call a method on the “fetch context”. Note that the promise is resolved in process_response, which is ‘fired’ in response to the headers being received, and after that the actual data is written to the body as each chunk comes in, reflecting the fact that the body of a response in Js is a ReadableStream

So coming back to our initial example, how is the response turned into a Blob?