-
Notifications
You must be signed in to change notification settings - Fork 39
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
Accept iterables and async iterables #72
Conversation
Right now, the following test fails: test('stops the pool from async error handler', async () => {
const timeouts = [10, 20, 30, 40, 50]
const { results } = await PromisePool
.for(timeouts)
.withConcurrency(2)
.handleError(async (_, __, pool) => {
pool.stop()
})
.process(async (timeout) => {
if (timeout < 30) {
throw new Error('stop the pool')
}
await pause(timeout)
return timeout
})
expect(results).toEqual([30])
}) @marcuspoehls Could you help me understand why |
@m93a good question. Intentionally I agree with you, the result should be If you want, you can adjust the mentioned test case to match your expected assertion (empty array). Does it succeed then? Maybe you’re right and the test is wrong |
Yes, if I assert an empty array, the test does succeed!
If you're anything like me, maybe you just ran the test, saw the result and thought "oh of course it's [30]" and changed the test? Sadly, I do this all the time 😁 Anyway, If my thinking is correct, the PR adds an asynchronous gap between the end of one task and the scheduling of another, hence making it possible to stop the next task in time. However, I tried to run the code example from #63 and my PR still didn't fix it, so it only fixes this small peculiarity. Let me know if you figure out why the test asserted |
Yes, please continue with the assumption that you fixed an issue in the test suite by adding the new functionality 😃👍 |
The implementation I wrote first was a bit unintuitive when it comes to calling I thought this would be a footgun, as developers would surely assume the pool would only iterate once it needs the next item, and not respecting this intuition would lead to logical errors in the code, so I moved the asynchronous This change (obviously) broke another test: test('useCorrespondingResults defaults results to notRun symbol', async () => {
const timeouts = [20, undefined, 10, 100]
const { results } = await PromisePool
.withConcurrency(1)
.for(timeouts)
.handleError((_error, _index, pool) => {
pool.stop()
})
.useCorrespondingResults()
.process(async (timeout) => {
if (timeout) {
await pause(timeout)
return timeout
}
throw new Error('did not work')
})
expect(results).toEqual([
20,
PromisePool.failed,
10,
PromisePool.notRun,
])
}) The pool in question has concurrency 1, so it should have stopped immediately after the failure (as per #63). However, the bug previously caused another timeout to be processed, hence the lonely expect(results).toEqual([
20,
PromisePool.failed,
PromisePool.notRun,
PromisePool.notRun,
]) the test passes. |
Hey, just wanted to let you know that the PR is good to go whenever you have some time! I haven't gotten around to adding the feature to the docs yet, but I realized they're actually in a separate repo. So, I thought it would be best to create another PR there once this one gets accepted, right? |
@m93a thanks for the heads up 😃 I’ll look through the changes as soon as I can, probably on the weekend |
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.
@m93a Thank you for your great work. Looks really nice. And you also fixed the issue where one extra item is processed. Really nice 🙌
I left some minor comments with requests for changes. Please have a look and then lets get this pull request merged 🙂
this.timeout = undefined | ||
this.concurrency = 10 | ||
this.shouldResultsCorrespond = false | ||
this.items = items ?? [] | ||
this.items = items ?? [] as any |
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.
Let’s the remove the as any
cast here too, please
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.
Thanks for catching this! It's a leftover from earlier, when I tried to hack around narrowing the type of items, but it didn't work out. I'll remove the cast!
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.
Thanks 👍
if (this.shouldUseCorrespondingResults()) { | ||
this.results()[index] = PromisePool.notRun | ||
} | ||
|
||
await this.waitForProcessingSlot() |
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 guess we can remove this and only keep the waitForProcessingSlot
at the end of this loop. This one doesn’t have any effect, does 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.
IIRC, this does nothing when items
is an array, but if it's an iterable it's quite vital. Then, the results array cannot be pre-filled with notRun
as we don't know the length (it could well be infinite). Imagine a scenario with an iterable containing 1,2,3,4,5
, the pool starts processing items 1,2,3
, then 1
and 3
finish and then the user stops the pool. The result would be [1, notRun, 3]
. If this code were to be removed, the second item would either be undefined
or <empty slot>
(not exactly sure how JS works here). I can add a comment explaining why the statement is needed, tho?
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 pull request changes the behavior of prefilling the results array to only prefill when items are an array. We’re not prefilling if the items come from an iterable. And we’re only prefilling for arrays if we want corresponding results.
I tested: tests are succeeding when removing this line which waits for a processing slot. Keeping the waiting line at the end though is crucial.
Fixes #63, fixes #71. Task list:
NOTE: This PR changes the return type of⚠️ breaking change ⚠️ for some TypeScript codebases!
pool.items()
from the narrower typeT[]
to the wider typeT[] | Iterable<T> | AsyncIterable<T>
, which might be a