-
Notifications
You must be signed in to change notification settings - Fork 7
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
[Possible bug] Empty iterator does not work after await
#35
Comments
I've added some more tests with the behavior I would expect here - but haven't yet had the time to find the root cause of the issue (and not sure if I will anytime soon unfortunately). All of the tests on this branch pass node: v16.10.0
npm: 7.24.0 |
I suspect you're running into an autoStart issue (see some of the closed issues here). There is an issue to change this behaviour: #25. |
So in doing working on #36 I've discovered that this issue is not entirely resolved by changing the In particular the bottom two of these following tests fail describe('An EmptyIterator should not emit until read from', () => {
it('no awaiting', async () => {
const iterator = new EmptyIterator();
await expect(await promisifyEventEmitter(iterator)).to.be.undefined;
});
it('awaiting undefined', async () => {
const iterator = new EmptyIterator();
await undefined;
await expect(await promisifyEventEmitter(iterator)).to.be.undefined;
});
it('awaiting promise', async () => {
const iterator = new EmptyIterator();
await Promise.resolve();
await expect(await promisifyEventEmitter(iterator)).to.be.undefined;
});
}); |
I'm thinking there are 2 solutions here
protected _sourceStarted: boolean;
protected taskBuffer: (() => {})[] = [];
protected _start() {
if (!this._sourceStarted) {
this._sourceStarted = true;
this.taskBuffer.forEach(task => taskScheduler(task))
this.taskBuffer = [];
}
}
protected _taskScheduler(task: () => {}) {
(this._sourceStarted ? taskScheduler : this.taskBuffer.push)(task);
} |
I'm fine with either option. @RubenVerborgh what option do you prefer? |
I now consider the default
Hence, we must not emit an The Hence it is also nonsensical to have So what I think @rubensworks wants is not for streams to not buffer, but for empty streams to (oh irony) not auto end. This might shed a very different light on #36. @rubensworks and @jeswr, would everything work as expected for you if:
|
That sounds like a good solution with expected outcomes @RubenVerborgh - I'll let you know if I have any more thoughts in modifying #36. This should only introduce a few breaks in the comunica 2.x - again related to |
I think Comunica needs to get the stream going by attaching an |
Exactly. Buffering itself is no problem.
I think this would be very valuable! |
@RubenVerborgh I made a start on it yesterday and will be try and do the rest today |
Amazing; really grateful to have you on this. |
Still working on this, I just wanted to check I'm on track with some of the decisions I have made in terms of other slight changes in behaviour as a result of this (I've done my best to keep it in line with the Readable API behaviour).
|
…when data might be available. So I think even the
Correct.
Likely, but I don't think it needs to be this strong. Currently, it is allowed for an iterator to have Are you introducing it for the case where an iterator has no items left, but no data listener is attached? So summarizing: I do think your statement is true / can be made true.
So I'm not sure if there is a necessity for that, but happy to hear arguments.
Why is that? This is internal state; the iterator can do as it wishes. |
TLDR; I agree with all the feedback; I have a lot more clarity on how to finish this off.
Yes
Yes I've done this, it only broke on test in the
Good thought - I think this is cleaner than the direction I was starting to head with the
I was making modifications so that in the
Sorry - I wasn't too clear here; I was referring to this; which was previously called when changing the state to this._readable = false;
this.removeAllListeners('readable');
this.removeAllListeners('data');
this.removeAllListeners('end'); |
I found that
—https://nodejs.org/api/stream.html so I think we can just do that same.
Yes, let's do that!
That's correct; those can only happen after emitting the final |
@RubenVerborgh Apologies for dragging this out - another places that I just realised the original implementation was going against the Calling readable.pause(), readable.unpipe(), or receiving backpressure will cause the readable.readableFlowing to be set as false, temporarily halting the flowing of events but not halting the generation of data. While in this state, attaching a listener for the 'data' event will not switch readable.readableFlowing to true. Similar to the changes around "For backward compatibility reasons, removing 'data' event handlers will not automatically pause the stream." this breaks a more tests around de re-attaching data listeners (though for the most part this could be resolved by replacing those re-attachments with calling Should I go ahead and make this change to align with the @rubensworks Do you imagine this will cause many problems downstream in Comunica (I shouldn't think so since it didn't have access to |
Indeed, shouldn't be a problem for Comunica. |
But the original implementation did not do
Fine with me. The boatload of tests was more to ensure consistent behavior wrt race conditions; what exactly the behavior is, can be determined for every major version—as long as it is self-consistent. |
The
empty
iterator does not emit events if something has beenawait
ed after its instantiation - for instance, the following tests time outwhilst the following works
Discovered in comunica/comunica#904
The text was updated successfully, but these errors were encountered: