Repository: tinylibs/tinybench
Pull request #136 (2024-10-18 10:38:34)

refactor: move async task detection at init

pkg-pr-new[bot]: 
[Open in Stackblitz](https://pkg.pr.new/template/9582b9f7-0fac-4cb9-9a9e-b3e7c94ec72d)


```
pnpm add https://pkg.pr.new/tinylibs/tinybench@136
```


_commit: <a href="https://github.com/tinylibs/tinybench/runs/31726632761"><code>5cad517</code></a>_


Patch:
diff --git a/src/task.ts b/src/task.ts
index 70f0d65..5421d97 100644
--- a/src/task.ts
+++ b/src/task.ts
@@ -15,7 +15,7 @@ import type {
 import {
   absoluteDeviation,
   average,
-  isAsyncTask,
+  isFnAsyncResource,
   medianSorted,
   quantileSorted,
   variance,
@@ -39,6 +39,11 @@ export default class Task extends EventTarget {
    */
   fn: Fn;
 
+  /**
+   * The task synchronous status
+   */
+  async: boolean;
+
   /*
    * The number of times the task function has been executed
    */
@@ -59,6 +64,7 @@ export default class Task extends EventTarget {
     this.bench = bench;
     this.name = name;
     this.fn = fn;
+    this.async = isFnAsyncResource(fn);
     this.opts = opts;
     // TODO: support signal in Tasks
   }
@@ -77,8 +83,6 @@ export default class Task extends EventTarget {
       }
     }
 
-    const asyncTask = await isAsyncTask(this);
-
     // TODO: factor out
     const executeTask = async () => {
       if (this.opts.beforeEach != null) {
@@ -86,7 +90,7 @@ export default class Task extends EventTarget {
       }
 
       let taskTime = 0;
-      if (asyncTask) {
+      if (this.async) {
         const taskStart = this.bench.now();
         await this.fn();
         taskTime = this.bench.now() - taskStart;
diff --git a/src/utils.ts b/src/utils.ts
index 255f987..565b814 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -1,4 +1,3 @@
-import type Task from './task';
 import type { Fn } from './types';
 
 export const nanoToMs = (nano: number) => nano / 1e6;
@@ -38,7 +37,7 @@ const isAsyncFunction = (
  * @param fn - the function to check
  * @returns true if the function is an async function or returns a promise
  */
-const isAsyncFnResource = async (fn: Fn): Promise<boolean> => {
+export const isFnAsyncResource = (fn: Fn): boolean => {
   if (fn == null) {
     return false;
   }
@@ -51,7 +50,8 @@ const isAsyncFnResource = async (fn: Fn): Promise<boolean> => {
     if (promiseLike) {
       // silence promise rejection
       try {
-        await fnCall;
+        // eslint-disable-next-line @typescript-eslint/no-empty-function
+        (fnCall as Promise<unknown>).then(() => {})?.catch(() => {});
       } catch {
         // ignore
       }
@@ -62,14 +62,6 @@ const isAsyncFnResource = async (fn: Fn): Promise<boolean> => {
   }
 };
 
-/**
- * An async task check helper considering runtime support async syntax and promise return
- *
- * @param task - the task to check
- * @returns true if the task is an async task
- */
-export const isAsyncTask = async (task: Task): Promise<boolean> => isAsyncFnResource(task?.fn);
-
 /**
  * Computes the average of a sample.
  *


Test patch:
diff --git a/test/isAsyncTask.test.ts b/test/isAsyncTask.test.ts
deleted file mode 100644
index d4c5c18..0000000
--- a/test/isAsyncTask.test.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-import { expect, test } from 'vitest';
-import { Bench, Task } from '../src';
-import { isAsyncTask } from '../src/utils';
-
-const bench = new Bench();
-
-test('isAsyncTask undefined', () => {
-  // @ts-expect-error: testing with undefined
-  expect(isAsyncTask(undefined)).resolves.toBe(false);
-});
-
-test('isAsyncTask null', () => {
-  // @ts-expect-error: testing with null
-  expect(isAsyncTask(null)).resolves.toBe(false);
-});
-
-test('isAsyncTask sync', () => {
-  const task = new Task(bench, 'foo', () => 1);
-  expect(isAsyncTask(task)).resolves.toBe(false);
-});
-
-test('isAsyncTask async', () => {
-  const task = new Task(bench, 'foo', async () => 1);
-  expect(isAsyncTask(task)).resolves.toBe(true);
-});
-
-test('isAsyncTask promise', () => {
-  const task = new Task(bench, 'foo', () => Promise.resolve(1));
-  expect(isAsyncTask(task)).resolves.toBe(true);
-});
-
-test('isAsyncTask promiseLike', () => {
-  const task = new Task(bench, 'foo', () => ({
-    then: (resolve: () => void) => setTimeout(resolve, 100),
-  }));
-  expect(isAsyncTask(task)).resolves.toBe(true);
-});
-
-test('isAsyncTask promise with error', () => {
-  const task = new Task(bench, 'foo', () => Promise.reject(new Error('foo')));
-  expect(isAsyncTask(task)).resolves.toBe(true);
-});
-
-test('isAsyncTask beforeEach with error', () => {
-  const task = new Task(bench, 'foo', async () => 1, {
-    beforeEach: () => {
-      throw new Error('foo');
-    },
-  });
-  expect(isAsyncTask(task)).resolves.toBe(true);
-});
-
-test('isAsyncTask beforeEach with promise error', () => {
-  const task = new Task(bench, 'foo', async () => 1, {
-    beforeEach: () => Promise.reject(new Error('foo')),
-  });
-  expect(isAsyncTask(task)).resolves.toBe(true);
-});
-
-test('isAsyncTask afterEach with error', () => {
-  const task = new Task(bench, 'foo', async () => 1, {
-    afterEach: () => {
-      throw new Error('foo');
-    },
-  });
-  expect(isAsyncTask(task)).resolves.toBe(true);
-});
-
-test('isAsyncTask afterEach with promise error', () => {
-  const task = new Task(bench, 'foo', async () => 1, {
-    afterEach: () => Promise.reject(new Error('foo')),
-  });
-  expect(isAsyncTask(task)).resolves.toBe(true);
-});
-
-test('isAsyncTask beforeAll with error', () => {
-  const task = new Task(bench, 'foo', async () => 1, {
-    beforeAll: () => {
-      throw new Error('foo');
-    },
-  });
-  expect(isAsyncTask(task)).resolves.toBe(true);
-});
-
-test('isAsyncTask beforeAll with promise error', () => {
-  const task = new Task(bench, 'foo', async () => 1, {
-    beforeAll: () => Promise.reject(new Error('foo')),
-  });
-  expect(isAsyncTask(task)).resolves.toBe(true);
-});
-
-test('isAsyncTask afterAll with error', () => {
-  const task = new Task(bench, 'foo', async () => 1, {
-    afterAll: () => {
-      throw new Error('foo');
-    },
-  });
-  expect(isAsyncTask(task)).resolves.toBe(true);
-});
-
-test('isAsyncTask afterAll with promise error', () => {
-  const task = new Task(bench, 'foo', async () => 1, {
-    afterAll: () => Promise.reject(new Error('foo')),
-  });
-  expect(isAsyncTask(task)).resolves.toBe(true);
-});
diff --git a/test/isFnAsyncResource.test.ts b/test/isFnAsyncResource.test.ts
new file mode 100644
index 0000000..de2a5e5
--- /dev/null
+++ b/test/isFnAsyncResource.test.ts
@@ -0,0 +1,65 @@
+import { expect, test } from 'vitest';
+import { isFnAsyncResource } from '../src/utils';
+
+test('isFnAsyncResource undefined', () => {
+  // @ts-expect-error: testing with undefined
+  expect(isFnAsyncResource(undefined)).toBe(false);
+});
+
+test('isFnAsyncResource null', () => {
+  // @ts-expect-error: testing with null
+  expect(isFnAsyncResource(null)).toBe(false);
+});
+
+test('isFnAsyncResource sync', () => {
+  expect(isFnAsyncResource(() => 1)).toBe(false);
+});
+
+test('isFnAsyncResource async', () => {
+  expect(isFnAsyncResource(async () => 1)).toBe(true);
+});
+
+test('isFnAsyncResource promise', () => {
+  expect(isFnAsyncResource(() => Promise.resolve(1))).toBe(true);
+});
+
+test('isFnAsyncResource async promise', () => {
+  expect(isFnAsyncResource(async () => Promise.resolve(1))).toBe(true);
+});
+
+test('isFnAsyncResource promise with error', () => {
+  expect(isFnAsyncResource(() => Promise.reject(new Error('error')))).toBe(
+    true,
+  );
+});
+
+test('isFnAsyncResource async promise with error', () => {
+  expect(
+    isFnAsyncResource(async () => Promise.reject(new Error('error'))),
+  ).toBe(true);
+});
+
+test('isFnAsyncResource promiseLike', () => {
+  expect(
+    isFnAsyncResource(() => ({
+      then: () => 1,
+    })),
+  ).toBe(true);
+  expect(isFnAsyncResource(() => ({ then: async () => 1 }))).toBe(true);
+  expect(isFnAsyncResource(() => ({ then: () => Promise.resolve(1) }))).toBe(
+    true,
+  );
+  expect(
+    isFnAsyncResource(() => ({ then: async () => Promise.resolve(1) })),
+  ).toBe(true);
+  expect(
+    isFnAsyncResource(() => ({
+      then: () => Promise.reject(new Error('error')),
+    })),
+  ).toBe(true);
+  expect(
+    isFnAsyncResource(() => ({
+      then: async () => Promise.reject(new Error('error')),
+    })),
+  ).toBe(true);
+});