Closed jakearchibald closed 3 years ago
Ah, I see there's note for some of this:
User agents are expected to actually populate the iterator’s queue asynchronously and possibly lazily, although this is not observable.
Although it is observable, as impacts the two examples above.
Looking at the spec a bit closer, it seems like creating a sortable name is pretty much all of the work needed to get the metadata, no?
If this becomes a true async iterator, it raises some questions that need to be answered in the prose:
Note that to answer these in the spec, you'll want to do it within the context of the "get the next iteration result" algorithm.
Right now it gathers up all the font representations in the initialisation steps. It does this on the main thread, which suggests sync I/O, which I don't think is the intention.
The implementation at the moment does not do this on the main thread. The list is gathered up on the first iteration.
Additionally, a true async iterator should probably check for permission in "get the next iteration result". Good feedback. That's what the current implementation does.
However, if returning everything at once is likely to be slow, then maybe there's a benefit to returning them one by one, so UI can update progressively. This might depend on how much up-front work is required to do the sorting vs computing the metadata. It'd be good to do some science here to influence the decision.
Based on how the current system APIs work, obtaining the font list is expensive, but once it's obtained, the enumeration itself is cheap. The fonts are then sorted. To sort the fonts, the whole list must be obtained.
This seems to point to a promise, then sequence API shape.
Now, given that we're building for the future, in the future, the operating systems could have a font cache daemon that could store font data in, say a database-like fashion and return fonts piecemeal and sorted and returned as a stream, leading into an async iteration interface. If system APIs change, the async iterators could be a great performance advantage.
I've tested the performance of async iterators and they don't cost a lot more: https://colab.research.google.com/drive/1C59LKSPY6ksZorcuLjrs40uABW1jX1Jn?usp=sharing
Should the API shape match the current system API shape, or should we build the API shape that sets us up to be the most efficient in the future?
Now, given that we're building for the future, in the future, the operating systems could have a font cache daemon that could store font data in, say a database-like fashion and return fonts piecemeal and sorted and returned as a stream, leading into an async iteration interface. If system APIs change, the async iterators could be a great performance advantage.
Is there any indication that this might happen in the foreseeable future?
If not, we always add another method that returns an iterator later.
When would developers want one-by-one results? Would that actually help applications in some way in "the future"? If there already are uses cases for getting all of them (and it seems to me you want to filter) I tend to agree with Jake on where to start.
If you wanted to provide a list of fonts the user has, and getting all the results takes 5 seconds, but getting the first result takes 100ms, then streaming them in would be nice. But since all the effort is up-front right now, we're kinda imagining use-cases.
Also, if a browser wants to provide agency to the user over which fonts to expose one-by-one wouldn't help either.
Yeah, although we may in future want to add things to the data object that involve more processing than the naming details (which is all that's currently there). Eg, we might want to provide some layout metrics.
I guess the easiest way we could do that is by adding a data.getExtendedData()
method which returns a promise. That's in-keeping with what we do to get the font bytes.
So, yeah, I'm more and more convinced this should be a sequence behind a promise.
The API now asynchronously returns a plain array, so the iteration is sync.
Right now it gathers up all the font representations in the initialisation steps. It does this on the main thread, which suggests sync I/O, which I don't think is the intention.
But also, gathering everything up front isn't really how async iteration works. If it's ok to gather everything up front, this should just be a sequence behind a promise.
However, if returning everything at once is likely to be slow, then maybe there's a benefit to returning them one by one, so UI can update progressively. This might depend on how much up-front work is required to do the sorting vs computing the metadata. It'd be good to do some science here to influence the decision.
If this becomes a true async iterator, it raises some questions that need to be answered in the prose:
And:
Additionally, a true async iterator should probably check for permission in "get the next iteration result".