Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The async iteration definition isn't very async #51

Closed
jakearchibald opened this issue Oct 8, 2020 · 10 comments
Closed

The async iteration definition isn't very async #51

jakearchibald opened this issue Oct 8, 2020 · 10 comments

Comments

@jakearchibald
Copy link

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:

// User has fonts a, c, and e.
const iterator = navigator.fonts.query();
const font1 = await iterator.next();
// font1 is a.
// But then, the user deletes font c.
const font2 = await iterator.next();
// What is font2?

And:

// User has fonts a, c, and e.
const iterator = navigator.fonts.query();
const font1 = await iterator.next();
// font1 is a.
// But then, the user installs font b.
const font2 = await iterator.next();
// What is font2?

Additionally, a true async iterator should probably check for permission in "get the next iteration result".

@jakearchibald
Copy link
Author

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.

@jakearchibald
Copy link
Author

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?

This was referenced Oct 8, 2020
@domenic
Copy link

domenic commented Oct 8, 2020

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.

@oyiptong
Copy link
Collaborator

oyiptong commented Oct 8, 2020

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?

@jakearchibald
Copy link
Author

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.

@annevk
Copy link

annevk commented Oct 8, 2020

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.

@jakearchibald
Copy link
Author

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.

@annevk
Copy link

annevk commented Oct 9, 2020

Also, if a browser wants to provide agency to the user over which fonts to expose one-by-one wouldn't help either.

@jakearchibald
Copy link
Author

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.

@inexorabletash
Copy link
Member

The API now asynchronously returns a plain array, so the iteration is sync.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants