@@ -40,7 +40,7 @@ export class Task extends EventTarget {
4040 runs = 0
4141
4242 /**
43- * The task synchronous status
43+ * The task asynchronous status
4444 */
4545 private readonly async : boolean
4646
@@ -222,62 +222,58 @@ export class Task extends EventTarget {
222222 }
223223 }
224224
225- // TODO: factor out
226225 let totalTime = 0 // ms
227226 const samples : number [ ] = [ ]
228227 const benchmarkTask = async ( ) => {
229- if ( this . fnOpts . beforeEach != null ) {
230- await this . fnOpts . beforeEach . call ( this , mode )
228+ if ( this . bench . opts . signal ?. aborted ) {
229+ return
231230 }
232-
233- let taskTime = 0 // ms;
234- if ( this . async ) {
235- const taskStart = this . bench . opts . now ( )
236- // eslint-disable-next-line no-useless-call
237- const fnResult = await this . fn . call ( this )
238- taskTime = this . bench . opts . now ( ) - taskStart
239-
240- const overriddenDuration = getOverriddenDurationFromFnResult ( fnResult )
241- if ( overriddenDuration != null ) {
242- taskTime = overriddenDuration
243- }
244- } else {
245- const taskStart = this . bench . opts . now ( )
246- // eslint-disable-next-line no-useless-call
247- const fnResult = this . fn . call ( this )
248- taskTime = this . bench . opts . now ( ) - taskStart
249-
250- const overriddenDuration = getOverriddenDurationFromFnResult ( fnResult )
251- if ( overriddenDuration != null ) {
252- taskTime = overriddenDuration
231+ try {
232+ if ( this . fnOpts . beforeEach != null ) {
233+ await this . fnOpts . beforeEach . call ( this , mode )
253234 }
254- }
255235
256- samples . push ( taskTime )
257- totalTime += taskTime
236+ let taskTime : number
237+ if ( this . async ) {
238+ ( { taskTime } = await this . measureOnce ( ) )
239+ } else {
240+ ( { taskTime } = this . measureOnceSync ( ) )
241+ }
258242
259- if ( this . fnOpts . afterEach != null ) {
260- await this . fnOpts . afterEach . call ( this , mode )
243+ samples . push ( taskTime )
244+ totalTime += taskTime
245+ } finally {
246+ if ( this . fnOpts . afterEach != null ) {
247+ await this . fnOpts . afterEach . call ( this , mode )
248+ }
261249 }
262250 }
263251
264252 try {
265- const limit = pLimit ( this . bench . threshold ) // only for task level concurrency
253+ let limit : ReturnType < typeof pLimit > | undefined // only for task level concurrency
254+ if ( this . bench . concurrency === 'task' ) {
255+ limit = pLimit ( this . bench . threshold )
256+ }
266257 const promises : Promise < void > [ ] = [ ] // only for task level concurrency
267258 while (
268259 // eslint-disable-next-line no-unmodified-loop-condition
269260 ( totalTime < time ||
270- samples . length + limit . activeCount + limit . pendingCount < iterations ) &&
261+ samples . length + ( limit ? .activeCount ?? 0 ) + ( limit ? .pendingCount ?? 0 ) < iterations ) &&
271262 ! this . bench . opts . signal ?. aborted
272263 ) {
273264 if ( this . bench . concurrency === 'task' ) {
274- promises . push ( limit ( benchmarkTask ) )
265+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
266+ promises . push ( ( limit ! ) ( benchmarkTask ) )
275267 } else {
276268 await benchmarkTask ( )
277269 }
278270 }
279271 if ( ! this . bench . opts . signal ?. aborted && promises . length > 0 ) {
280272 await Promise . all ( promises )
273+ } else if ( promises . length > 0 ) {
274+ // Abort path
275+ // eslint-disable-next-line no-void
276+ void Promise . allSettled ( promises )
281277 }
282278 } catch ( error ) {
283279 return { error }
@@ -310,52 +306,42 @@ export class Task extends EventTarget {
310306 }
311307 }
312308
313- // TODO: factor out
314- let totalTime = 0 // ms
309+ let totalTime = 0
315310 const samples : number [ ] = [ ]
316311 const benchmarkTask = ( ) => {
317- if ( this . fnOpts . beforeEach != null ) {
318- const beforeEachResult = this . fnOpts . beforeEach . call ( this , mode )
319- invariant (
320- ! isPromiseLike ( beforeEachResult ) ,
321- '`beforeEach` function must be sync when using `runSync()`'
322- )
323- }
324-
325- let taskTime = 0 // ms;
326-
327- const taskStart = this . bench . opts . now ( )
328- // eslint-disable-next-line no-useless-call
329- const fnResult = this . fn . call ( this )
330- taskTime = this . bench . opts . now ( ) - taskStart
331-
332- invariant (
333- ! isPromiseLike ( fnResult ) ,
334- 'task function must be sync when using `runSync()`'
335- )
336-
337- const overriddenDuration = getOverriddenDurationFromFnResult ( fnResult )
338- if ( overriddenDuration != null ) {
339- taskTime = overriddenDuration
312+ if ( this . bench . opts . signal ?. aborted ) {
313+ return
340314 }
315+ try {
316+ if ( this . fnOpts . beforeEach != null ) {
317+ const beforeEachResult = this . fnOpts . beforeEach . call ( this , mode )
318+ invariant (
319+ ! isPromiseLike ( beforeEachResult ) ,
320+ '`beforeEach` function must be sync when using `runSync()`'
321+ )
322+ }
341323
342- samples . push ( taskTime )
343- totalTime += taskTime
344-
345- if ( this . fnOpts . afterEach != null ) {
346- const afterEachResult = this . fnOpts . afterEach . call ( this , mode )
347- invariant (
348- ! isPromiseLike ( afterEachResult ) ,
349- '`afterEach` function must be sync when using `runSync()`'
350- )
324+ const { taskTime } = this . measureOnceSync ( )
325+
326+ samples . push ( taskTime )
327+ totalTime += taskTime
328+ } finally {
329+ if ( this . fnOpts . afterEach != null ) {
330+ const afterEachResult = this . fnOpts . afterEach . call ( this , mode )
331+ invariant (
332+ ! isPromiseLike ( afterEachResult ) ,
333+ '`afterEach` function must be sync when using `runSync()`'
334+ )
335+ }
351336 }
352337 }
353338
354339 try {
355340 while (
356341 // eslint-disable-next-line no-unmodified-loop-condition
357- totalTime < time ||
358- samples . length < iterations
342+ ( totalTime < time ||
343+ samples . length < iterations ) &&
344+ ! this . bench . opts . signal ?. aborted
359345 ) {
360346 benchmarkTask ( )
361347 }
@@ -377,6 +363,34 @@ export class Task extends EventTarget {
377363 return { samples }
378364 }
379365
366+ private async measureOnce ( ) : Promise < { fnResult : ReturnType < Fn > ; taskTime : number } > {
367+ const taskStart = this . bench . opts . now ( )
368+ // eslint-disable-next-line no-useless-call
369+ const fnResult = await this . fn . call ( this )
370+ let taskTime = this . bench . opts . now ( ) - taskStart
371+ const overriddenDuration = getOverriddenDurationFromFnResult ( fnResult )
372+ if ( overriddenDuration != null ) {
373+ taskTime = overriddenDuration
374+ }
375+ return { fnResult, taskTime }
376+ }
377+
378+ private measureOnceSync ( ) : { fnResult : ReturnType < Fn > ; taskTime : number } {
379+ const taskStart = this . bench . opts . now ( )
380+ // eslint-disable-next-line no-useless-call
381+ const fnResult = this . fn . call ( this )
382+ let taskTime = this . bench . opts . now ( ) - taskStart
383+ invariant (
384+ ! isPromiseLike ( fnResult ) ,
385+ 'task function must be sync when using `runSync()`'
386+ )
387+ const overriddenDuration = getOverriddenDurationFromFnResult ( fnResult )
388+ if ( overriddenDuration != null ) {
389+ taskTime = overriddenDuration
390+ }
391+ return { fnResult, taskTime }
392+ }
393+
380394 /**
381395 * merge into the result object values
382396 * @param result - the task result object to merge with the current result object values
@@ -406,7 +420,7 @@ export class Task extends EventTarget {
406420 error ?: Error
407421 latencySamples ?: number [ ]
408422 } ) : void {
409- if ( latencySamples ) {
423+ if ( latencySamples && latencySamples . length > 0 ) {
410424 this . runs = latencySamples . length
411425 const totalTime = latencySamples . reduce ( ( a , b ) => a + b , 0 )
412426
@@ -418,16 +432,17 @@ export class Task extends EventTarget {
418432 // Throughput statistics
419433 const throughputSamples = latencySamples
420434 . map ( sample =>
421- sample !== 0 ? 1000 / sample : 1000 / latencyStatistics . mean
435+ sample !== 0
436+ ? 1000 / sample
437+ : latencyStatistics . mean !== 0
438+ ? 1000 / latencyStatistics . mean
439+ : 0
422440 ) // Use latency average as imputed sample
423441 . sort ( ( a , b ) => a - b )
424442 const throughputStatistics = getStatisticsSorted ( throughputSamples )
425443
426- if ( this . bench . opts . signal ?. aborted ) {
427- return
428- }
429-
430444 this . mergeTaskResult ( {
445+ aborted : this . bench . opts . signal ?. aborted ?? false ,
431446 critical : latencyStatistics . critical ,
432447 df : latencyStatistics . df ,
433448 hz : throughputStatistics . mean ,
@@ -481,7 +496,9 @@ function getOverriddenDurationFromFnResult (
481496 fnResult != null &&
482497 typeof fnResult === 'object' &&
483498 'overriddenDuration' in fnResult &&
484- typeof fnResult . overriddenDuration === 'number'
499+ typeof fnResult . overriddenDuration === 'number' &&
500+ Number . isFinite ( fnResult . overriddenDuration ) &&
501+ fnResult . overriddenDuration >= 0
485502 ) {
486503 return fnResult . overriddenDuration
487504 }
0 commit comments