-
-
Notifications
You must be signed in to change notification settings - Fork 192
Fix timeout-dependent return types of add
and addAll
#206
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
base: main
Are you sure you want to change the base?
Changes from 5 commits
9dcb557
c1d076b
16791ca
0d8fcb6
520dc1b
8f20196
1dc9294
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ import {EventEmitter} from 'eventemitter3'; | |
import pTimeout, {TimeoutError} from 'p-timeout'; | ||
import {type Queue, type RunFunction} from './queue.js'; | ||
import PriorityQueue from './priority-queue.js'; | ||
import {type QueueAddOptions, type Options, type TaskOptions} from './options.js'; | ||
import {type QueueAddOptions, type Options, type TaskOptions, type WithoutTimeout, type WithoutTimeoutThrow, type WithTimeoutThrow} from './options.js'; | ||
|
||
type Task<TaskResultType> = | ||
| ((options: TaskOptions) => PromiseLike<TaskResultType>) | ||
|
@@ -50,7 +50,6 @@ export default class PQueue<QueueType extends Queue<RunFunction, EnqueueOptionsT | |
*/ | ||
timeout?: number; | ||
|
||
// TODO: The `throwOnTimeout` option should affect the return types of `add()` and `addAll()` | ||
constructor(options?: Options<QueueType, EnqueueOptionsType>) { | ||
super(); | ||
|
||
|
@@ -231,15 +230,32 @@ export default class PQueue<QueueType extends Queue<RunFunction, EnqueueOptionsT | |
/** | ||
Adds a sync or async task to the queue. Always returns a promise. | ||
*/ | ||
async add<TaskResultType>(function_: Task<TaskResultType>, options: {throwOnTimeout: true} & Exclude<EnqueueOptionsType, 'throwOnTimeout'>): Promise<TaskResultType>; | ||
async add<TaskResultType>(function_: Task<TaskResultType>, options?: Partial<EnqueueOptionsType>): Promise<TaskResultType | void>; | ||
async add<TaskResultType>(function_: Task<TaskResultType>, options: Partial<EnqueueOptionsType> = {}): Promise<TaskResultType | void> { | ||
async add<TaskResultType>( | ||
function_: Task<TaskResultType>, | ||
options?: WithoutTimeout<Partial<EnqueueOptionsType>> | ||
): Promise<TaskResultType>; | ||
async add<TaskResultType>( | ||
function_: Task<TaskResultType>, | ||
options: WithTimeoutThrow<Partial<EnqueueOptionsType>> | ||
): Promise<TaskResultType>; | ||
async add<TaskResultType>( | ||
function_: Task<TaskResultType>, | ||
options: WithoutTimeoutThrow<Partial<EnqueueOptionsType>> | ||
): Promise<TaskResultType | void>; | ||
async add<TaskResultType>( | ||
function_: Task<TaskResultType>, | ||
options: Partial<EnqueueOptionsType> = {}, | ||
): Promise<TaskResultType | void> { | ||
options = { | ||
timeout: this.timeout, | ||
throwOnTimeout: this.#throwOnTimeout, | ||
...options, | ||
}; | ||
|
||
if (!options.timeout && options.throwOnTimeout) { | ||
console.warn('You specified `throwOnTimeout=true` without defining `timeout`.'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should throw an error here, and make the options mutually exclusive. |
||
} | ||
|
||
return new Promise((resolve, reject) => { | ||
this.#queue.enqueue(async () => { | ||
this.#pending++; | ||
|
@@ -287,15 +303,19 @@ export default class PQueue<QueueType extends Queue<RunFunction, EnqueueOptionsT | |
*/ | ||
async addAll<TaskResultsType>( | ||
functions: ReadonlyArray<Task<TaskResultsType>>, | ||
options?: {throwOnTimeout: true} & Partial<Exclude<EnqueueOptionsType, 'throwOnTimeout'>>, | ||
options?: WithoutTimeout<Partial<EnqueueOptionsType>>, | ||
): Promise<TaskResultsType[]>; | ||
async addAll<TaskResultsType>( | ||
functions: ReadonlyArray<Task<TaskResultsType>>, | ||
options: WithTimeoutThrow<Partial<EnqueueOptionsType>> | ||
): Promise<TaskResultsType[]>; | ||
async addAll<TaskResultsType>( | ||
functions: ReadonlyArray<Task<TaskResultsType>>, | ||
options?: Partial<EnqueueOptionsType>, | ||
options: WithoutTimeoutThrow<Partial<EnqueueOptionsType>>, | ||
): Promise<Array<TaskResultsType | void>>; | ||
async addAll<TaskResultsType>( | ||
functions: ReadonlyArray<Task<TaskResultsType>>, | ||
options?: Partial<EnqueueOptionsType>, | ||
options: Partial<EnqueueOptionsType> = {}, | ||
): Promise<Array<TaskResultsType | void>> { | ||
return Promise.all(functions.map(async function_ => this.add(function_, options))); | ||
} | ||
|
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.
Damn, as soon as I submitted the PR I realized we can't remove this just yet. The options type passed to the constructor will need to be "remembered" so we can reuse it when determining the return types for
add
andaddAll
. Because you could specifythrowOnTimeout=true
in the constructor and then onlytimeout=number
when callingadd
. I'll see if I can figure out a solution...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.
Okay, I couldn't find a solution so I added the todo back. I've set up this simplified example in TS Playground that features my best attempt. I also tried adding a second generic type parameter to
add
but that didn't work either. It's unclear to me if I'm lacking the TS knowledge or if we are brushing up against the limits of TS. Maybe someone else is able to solve this; should I open a separate issue for this todo?I still think there's value in this PR though, since it at least corrects the types for the
add
method when looked at in isolation. It just won't respect the options passed earlier to the constructor, which was already the case before this PR. However thevoid
will now surface differently to before this PR which might come as a surprise to people defining constructor options:So I guess it's a question of where you prefer the
void
type surprise to happen? Personally I think it's better to haveadd
be correct in isolation and let the constructor options people deal with forcing the type to bevoid
. But I can understand if you preferadd
to incorrectly returnvoid
if that is a "safer" type surprise for people to deal with. Either way it's a mess!