Closed jakearchibald closed 5 years ago
So in that proposed syntax if you want to define a method returning an async iterable you'd have to define a separate interface for that return type, and return that instead? Or would async_iterable<Something>
also be a valid return type directly? I guess defining the extra interface would be easy enough, just in my mind I was seeing this more as the async equivalent of a method that would otherwise return a sequence<Something>
.
But either way, yes, I definitely want some way of being able to return async interables in specs. So whatever shape that takes, sounds good to me.
@mkruisselbrink I'm not sure I understand, so this reply may be trash 😀
if you want to define a method returning an async iterable you'd have to define a separate interface for that return type, and return that instead? Or would
async_iterable<Something>
also be a valid return type directly?
You'd have to define Something
, or use an existing type. This isn't a return type, it's more like how iterators are currently defined:
interface interface_identifier {
iterable<value_type>;
};
But the prose associated with iterable
is pretty basic, and I think you'd struggle to write something like fibonacci counter with it (not that it's been a problem as far as I know).
Async iterables are complicated straight away as you need to deal with "in parallel" and task queueing, else there's no point in it being async, so you really need to a way to express how the values are "pulled".
Maybe I'm mixing up async iterables with async iterators. Afaik a interface that specifies iterable<bla>
results in several methods being defined on the interface that all return a iterator object (and an iterable can be iterated multiple times, while each iterator object is single use). Would the same be true for async_iterable<bla>
? I.e. it would result in a entries()
method that returns an async iterator object? I guess what I was thinking of would be being able to define a method that returns an async iterator object directly, rather than having to have an iterable in between. I.e. just define a method that behaves like a async generator function, where I'd expect to have all the in parallel and task queuing steps in the prose/algorithm for my method, rather than having the extra layer/state of an iterable in between.
Restating:
iterable<T>
where the interface itself is an iterablesequence<T>
which is iterableIn the OP, async_iterable<T>
is like iterable<T>
but only (so far) defines the [@@asyncIterable]()
method, so you get:
let sum = 0;
for await (const i of slowCounter) { sum += i; }
Whereas @mkruisselbrink is after:
let sum = 0;
for await (const i of someObject.getSlowCounter()) { sum += i; }
(Aside: for sync iterable<T>
we provide [@@iterator]()
as the bare minimum, as well as entries()
, values()
, keys()
and forEach()
, following the common methods on Array/Map/Set. So far as I know, we haven't evolved such a pattern for async iterables in the platform.)
With the OP proposal, you'd need to write:
interface Whatever {
WhateverSomethingAsyncIterator getSomethings();
}
interface WhateverSomethingAsyncIterator {
async_iterable<Something>;
}
Which, if you're thinking of this as an async replacement for sequence<T> getSomethings()
is fairly verbose.
Both use cases seem legitimate. I guess I would assume async_iterable<T>
would behave per the OP, and we'd want something like async_sequence<T>
for the second use case?
@inexorabletash cheers! I'm on the same track now.
Unless I'm missing something, the async_sequence
would still need generator-like prose to return items and close the iterator. And with that, you get the problem of task queues vs webidl.
There is already some intertwinedness, so I wouldn't worry too much about it. Alternative you could define some host hooks that HTML would fill in.
I'm prototyping something here as part of https://github.com/domenic/async-local-storage/issues/6 which should probably get upstreamed eventually. Right now I'm manually pushing stuff into a queue, as does Jake's OP. Interestingly that's not really how async iterators work... they instead "pull", only running a set of steps each time next() is called, lazily. This gives built-in backpressure, which both of us are ignoring, I think. (Unless I misunderstood the "yield" in Jake's OP.)
I was thinking "yield" would behave as it does in a generator, so the prose would pause until the next pull.
It'd be nice if we could create async iterators in a similar way to iterators.
The prose would need to be a little more complicated than
iterable
, probably written like a generator.For example:
To asynchronously iterate over a
SlowCounter
, run the following steps:"Yield" would queue a task on the given task source to resolve the iteration's promise with the given value. The algorithm would not advance until the next iteration was requested.
I guess it's tricky as task sources are an HTML thing, but I can't see how it can be handled correctly otherwise.