-
Notifications
You must be signed in to change notification settings - Fork 24
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
Revamp Subscription
, Stream
APIs to make combinators easy
#693
Comments
This sounds like a nice simplification. |
The one difficulty is that it is annoying to have to save the back reference to Subscription when creating it by hand: let subscription = {
*next() {
// implement next
},
*[Symbol.iterator]() { return subscription; },
};
return subscription; Perhaps we can make a helper "createSubscription()" that helps you do it my just accepting the content of next: createSubscription(function* next() {
// implementation of next operation.
}); So for example, the implementation of Subscription in Channel would be something like: subscription: createSubscription(function* next() {
let message = items.pop();
if (message) {
return message;
} else {
return yield* action<Item>(function* (resolve) {
consumers.unshift(resolve);
});
}
}), Thus far, it has been extremely rare to create subscriptions by hand, since most of the time, I just end up using |
I'm going to advocate that we first disambiguate streams from subscriptions (you should never need to deal with a subscription directly) and then we can add make subscriptions streamable individually which will be possible if they have separate API. |
> fixes #693 In order to simplify working with streams and subscriptions, we need to be able to disambiguate them easily. The relationship between `Stream` and `Subscription` is the same as that between `Iterable` -> `Iterator` and `AsyncIterable` -> `AsyncIterator`. This converts `Subscription` into an API that is highly analogous to `AsyncIterator`. The `next()` method returns an `Operation` which yields an `IteratorResult` So in the same way `AsyncIterator` has: ```ts next(): Promise<IteratorResult>; ``` The `Subscription` api will look like: ```ts next(): Operation<IteratorResult>; ``` Once we have this in place, it is trivial to create a stream out of any Subscription: ```ts function streamable(subscription: Subscription): Stream { return ({ *[Symbol.iterator]() { return subscription; } }); } ```
> fixes #693 In order to simplify working with streams and subscriptions, we need to be able to disambiguate them easily. The relationship between `Stream` and `Subscription` is the same as that between `Iterable` -> `Iterator` and `AsyncIterable` -> `AsyncIterator`. This converts `Subscription` into an API that is highly analogous to `AsyncIterator`. The `next()` method returns an `Operation` which yields an `IteratorResult` So in the same way `AsyncIterator` has: ```ts next(): Promise<IteratorResult>; ``` The `Subscription` api will look like: ```ts next(): Operation<IteratorResult>; ``` Once we have this in place, it is trivial to create a stream out of any Subscription: ```ts function streamable(subscription: Subscription): Stream { return ({ *[Symbol.iterator]() { return subscription; } }); } ```
Closed by #696 |
Right now a stream is a context-free operation that when run, yields a subscription that is coupled to the current context. And the subscription will yield the next item, and importantly, when that context passes away, the subscription will be automatically be released and do whatever cleanup is necessary.
It looks like this:
But this is annoying because streams and subscriptions are very similar in the way they behave. The only difference is that one is "concrete" and the other is abstract. However, we would expect the same kind of result for both
yield* first(stream)
andyield* first(subscription)
and so we'd like forfirst
not to care if it is operating on aStream
or aSubscription
. The same applies tofilter
,map
,forEach
, etc....I think the answer might lie in more closely aligning
Stream
andSubscription
withIterable
andIterator
andAsyncIterable
, andAsyncIterator
which is what they really are:Stream === OperationIterable
,Subscription === Operation Iterator
.AsyncIterator
s areAsyncIterable
by returning themselves for the[Symbol.asyncIterator]()
method.Generator
s areIterable
by returning themselves for the[Symbol.iterator]()
method.I propose that we make
Subscription
implementStream
, by returning itself for the[Symbol.iterator]
method. That way, all combinators can be implemented to work onStream
, and a live subscription is just a special type of stream to which you are already subscribed. The new types would look like:Now, if we define
forEach
to work on aStream
:Then, it will work on both a stream, and a live subscription. The same applies for all our combinators you would care to write.
The text was updated successfully, but these errors were encountered: