-
Notifications
You must be signed in to change notification settings - Fork 74
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
fix: ensure catchError functions always return source iterator #373
base: master
Are you sure you want to change the base?
fix: ensure catchError functions always return source iterator #373
Conversation
|
That's strange. Looking into it. |
I don't know what's going on. It doesn't look like yarn is fetching eslint at all. It's possible something in the request chain is affected by the numerous outages today. |
4e40c11
to
acd8fbd
Compare
Tests passed, LGTM. @mattpodwysocki any thoughts? |
Could you please take care of the change log and publish an update once this is merged? Thanks 🙂 |
Yes, changelog happens automatically when we publish. @mattpodwysocki requests a test to exercise the new behavior. The tests are in the I think something like this should work for Iterable and can adapt for AsyncIterable: test('Iterable#catchError calls return() on source iterator when no error', () => {
let done = false;
let returnCalled = false;
const xs = {
[Symbol.iterator]() { return xs; },
next: () => (done = !done) ? { value: 0 } : { done: true },
return: () => { returnCalled = true; }
};
const res = from(xs).pipe(catchError((_: Error) => of('foo')));
expect(sequenceEqual(res, range(0, 1))).toBeTruthy();
expect(returnCalled).toBeTruthy();
}); You can run the tests against the source via: yarn test -t src --tests spec/iterable-operators/catcherror-spec.ts Or to test against the compiled code: # Compile all the output targets
yarn build
# Run the given test against each target
yarn test --tests spec/iterable-operators/catcherror-spec.ts |
How do I debug tests? The tests fail for some reason, and I can't figure out how to attach a debugger, or even use console.log statements. Also, if unrelated, I can't use jest in tests or I get |
You can debug the tests in VSCode via the "Debug Unit Tests" launch target.
|
I don't think we typically rely on the jasmine spy functions, but I do see one place we import and use them in |
We pass yarn test -t src --tests spec/iterable-operators/catcherror-spec.ts --verbose I suspect this is why you weren't seeing your |
|
||
try { | ||
while (count > 0 && !(next = it.next()).done) { | ||
count--; | ||
} | ||
if (count <= 0) { | ||
while (!(next = it.next()).done) { | ||
yield next.value; | ||
} | ||
} | ||
} finally { | ||
returnIterator(it); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR has me wondering if the style of explicitly constructing and enumerating the iterator should be avoided.
Perhaps we should refactor to be something like this?
let count = this._count;
for (const value of this._source) {
if (--count < 0) {
yield value;
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I fully agree with that. Virtually every manual call to [Symbol.(async)Iterator]()
is susceptible to this issue.
That's for another MR, though. I need this change to go through first (it's left a big "TODO: update ix" at work), but could help with fixing the remaining ones after this.
2eae459
to
c31a8df
Compare
I'm debugging the tests and it looks like we've stumbled across a closure compiler bug (wouldn't be the first time). I'll test if changing the implementation to not iterate manually fixes the issue. |
Oh actually, I know the issue. Closure compiler's iterator rewriting replaces calls like |
Ah, I spoke too soon. It is indeed a closure compiler bug: // test.js
function first(xs) {
for (let x of xs) {
return x;
}
}
let count = 0;
let returnCalled = false;
const xs = {
[Symbol.iterator]() { return xs; },
next() {
if (count < 3) {
return { value: count++, done: false };
}
return { done: true };
},
return() {
returnCalled = true;
return { done: true };
}
};
first(xs);
console.log('return called:', returnCalled);
|
How do we proceed here? We could |
Your options A and B won't work, because the issue isn't with our code -- even if we implement a manual iterator, the issue is that closure compiler doesn't call the The reason for closure-compiler is that historically, its (async)iterator and Promise polyfills have been both faster and smaller than alternatives, so we've tolerated its difficulties. That said, I haven't compared it to the downleveled iterators that use |
Why does Ix provide es5 code at all in the first place? |
catchError
currently swallows return signals. Instead of explicitly returning only if the source iterator is done or an error is thrown, always return it once done.I have briefly tried to add unit tests for this, but I couldn't find any other tests for similar behaviour and couldn't manage the juggle to spy on the return function.