Intent to Implement Streams in Firefox
In the next few months I intend to begin implementing the Streams API in the Gecko codebase.
Google has been working on this spec for a while and has shipped a small subset of the API for the fetch Response body. Over the last few months I’ve worked with others at Mozilla and Google to try to evaluate if this spec is suitable for Firefox.
This evaluation resulted in three main concerns that I believe have been addressed.
The current spec proposes a
read() function that always returns a promise.
We had concerns about possible performance problems with forcing an asynchronous
callback and creating an additional object on every read.
To investigate this issue we implemented a couple rough benchmarks to try to determine what kind of performance could be expected. The details here are a bit long, so I wrote a separate post describing the async read evaluation.
The end conclusion, however, was the the currently proposed
read() should not
be a problem in practice.
Algorithms Operating on the Public API
As currently proposed, the Streams spec defines certain algorithms in terms of publicly exposed functions.
pipeTo() is essentially spec’d as:
1 2 3 4 5 6 7 8 9 10 11
This calls the public
.read() function on the source stream’s reader and the
.write() function on the sink stream.
1 2 3 4 5
Unfortunately, providing this kind of feature makes certain kinds of optimizations in the browser exceptionally hard.
For example, consider if the source stream and sink stream being piped together are both C++ implemented streams. Perhaps the source is a Response.body and the sink is a file stream (which is not currently spec’d).
Even if we were to add checking for monkey patching vs unmodified public functions, the checks add complexity and introduce performance penalties themselves.
To avoid these kinds of issues, it’s cleanest if algorithms like
pipeTo() are instead
spec’d to operate on internal data. Instead of calling
.read() they would “read from
the internal source”. Instead of calling
.write() they would “write to the
While the original design was written in terms of public functions, I believe we have consensus to move to an internal object design.
Transferring Streams Between Threads
Finally, we feel it’s important that the spec supports transferring streams from one thread to another. So for example, a fetch Response could be initiated on the main thread in a window, the body stream passed to a Worker to process the data, and finally streamed to storage.
Again, I believe we have an informal agreement on how to move forward. The thread transfer issue outlines how the stream would be locked on the current thread and conceptually drained as the new thread reads it.
This generally needs many of the same things required for off-main-thread
pipeTo(). For example, in addition to the internal object design mentioned above,
the spec also uses a locked reader design which further supports these kinds of
With these concerns in mind I intend to begin implementing Streams in Gecko.
In addition to providing an implementation, I believe this will allow me to be more active in shaping the spec itself. Many parts of the spec are stable, but things are still changing. Work is proceeding on writable streams, byte streams, and transforms. It’s in everyone’s interest to have more browser vendors actively engaged in working with the spec to find issues early.
Shipping code will depend on how things finalize in the spec and the code, of course. My hope, however, is to have an implementation of the fetch Response body stream shipping by the end of the year. This is the same subset of the spec currently shipped by Chrome.
Please let me know if you have any questions or concerns.
Thank you to Domenic Denicola and Andrew Overholt for reviewing an earlier draft of this post.