+18
-13
@@ -52,7 +52,8 @@ 'use strict'; | ||
| } | ||
| function iterateSync(generator) { | ||
| const DEFAULT_ON_YIELD = (value) => value; | ||
| function iterateSync(generator, onYield = DEFAULT_ON_YIELD) { | ||
| let current = generator.next(); | ||
| while (!current.done) { | ||
| try { | ||
| current = generator.next(unwrapYield(current.value)); | ||
| current = generator.next(unwrapYield(onYield(current.value, false))); | ||
| } catch (err) { | ||
@@ -64,7 +65,7 @@ current = generator.throw(err); | ||
| } | ||
| async function iterateAsync(generator) { | ||
| async function iterateAsync(generator, onYield = DEFAULT_ON_YIELD) { | ||
| let current = generator.next(); | ||
| while (!current.done) { | ||
| try { | ||
| current = generator.next(await unwrapYield(current.value, true)); | ||
| current = generator.next(await unwrapYield(onYield(current.value, true), true)); | ||
| } catch (err) { | ||
@@ -76,20 +77,20 @@ current = generator.throw(err); | ||
| } | ||
| function fromGeneratorFn(generatorFn) { | ||
| function fromGeneratorFn(generatorFn, options) { | ||
| return fromObject({ | ||
| name: generatorFn.name, | ||
| async(...args) { | ||
| return iterateAsync(generatorFn.apply(this, args)); | ||
| return iterateAsync(generatorFn.apply(this, args), options?.onYield); | ||
| }, | ||
| sync(...args) { | ||
| return iterateSync(generatorFn.apply(this, args)); | ||
| return iterateSync(generatorFn.apply(this, args), options?.onYield); | ||
| } | ||
| }); | ||
| } | ||
| function quansync(options) { | ||
| if (isThenable(options)) | ||
| return fromPromise(options); | ||
| if (typeof options === "function") | ||
| return fromGeneratorFn(options); | ||
| function quansync(input, options) { | ||
| if (isThenable(input)) | ||
| return fromPromise(input); | ||
| if (typeof input === "function") | ||
| return fromGeneratorFn(input, options); | ||
| else | ||
| return fromObject(options); | ||
| return fromObject(input); | ||
| } | ||
@@ -101,6 +102,10 @@ function toGenerator(promise) { | ||
| } | ||
| function* getIsAsync() { | ||
| return !!(yield GET_IS_ASYNC); | ||
| } | ||
| exports.GET_IS_ASYNC = GET_IS_ASYNC; | ||
| exports.QuansyncError = QuansyncError; | ||
| exports.getIsAsync = getIsAsync; | ||
| exports.quansync = quansync; | ||
| exports.toGenerator = toGenerator; |
+20
-4
@@ -1,4 +0,14 @@ | ||
| import { QuansyncInput, QuansyncFn, QuansyncGenerator } from './types.cjs'; | ||
| export { QuansyncAwaitableGenerator, QuansyncGeneratorFn, QuansyncInputObject } from './types.cjs'; | ||
| import type { QuansyncFn, QuansyncGenerator, QuansyncGeneratorFn, QuansyncInputObject, QuansyncOptions } from './types.cjs'; | ||
| export { QuansyncAwaitableGenerator, QuansyncInput } from './types.cjs'; | ||
| declare const GET_IS_ASYNC: unique symbol; | ||
@@ -11,3 +21,4 @@ declare class QuansyncError extends Error { | ||
| */ | ||
| declare function quansync<Return, Args extends any[] = []>(options: QuansyncInput<Return, Args> | Promise<Return>): QuansyncFn<Return, Args>; | ||
| declare function quansync<Return, Args extends any[] = []>(input: QuansyncInputObject<Return, Args>): QuansyncFn<Return, Args>; | ||
| declare function quansync<Return, Args extends any[] = []>(input: QuansyncGeneratorFn<Return, Args> | Promise<Return>, options?: QuansyncOptions): QuansyncFn<Return, Args>; | ||
| /** | ||
@@ -17,3 +28,8 @@ * Converts a promise to a Quansync generator. | ||
| declare function toGenerator<T>(promise: Promise<T> | QuansyncGenerator<T> | T): QuansyncGenerator<T>; | ||
| /** | ||
| * @returns `true` if the current context is async, `false` otherwise. | ||
| */ | ||
| declare function getIsAsync(): Generator<typeof GET_IS_ASYNC, boolean, unknown>; | ||
| export { GET_IS_ASYNC, QuansyncError, QuansyncFn, QuansyncGenerator, QuansyncInput, quansync, toGenerator }; | ||
| export { GET_IS_ASYNC, QuansyncError, getIsAsync, quansync, toGenerator }; | ||
| export type { QuansyncFn, QuansyncGenerator, QuansyncGeneratorFn, QuansyncInputObject, QuansyncOptions }; |
+20
-4
@@ -1,4 +0,14 @@ | ||
| import { QuansyncInput, QuansyncFn, QuansyncGenerator } from './types.mjs'; | ||
| export { QuansyncAwaitableGenerator, QuansyncGeneratorFn, QuansyncInputObject } from './types.mjs'; | ||
| import type { QuansyncFn, QuansyncGenerator, QuansyncGeneratorFn, QuansyncInputObject, QuansyncOptions } from './types.mjs'; | ||
| export { QuansyncAwaitableGenerator, QuansyncInput } from './types.mjs'; | ||
| declare const GET_IS_ASYNC: unique symbol; | ||
@@ -11,3 +21,4 @@ declare class QuansyncError extends Error { | ||
| */ | ||
| declare function quansync<Return, Args extends any[] = []>(options: QuansyncInput<Return, Args> | Promise<Return>): QuansyncFn<Return, Args>; | ||
| declare function quansync<Return, Args extends any[] = []>(input: QuansyncInputObject<Return, Args>): QuansyncFn<Return, Args>; | ||
| declare function quansync<Return, Args extends any[] = []>(input: QuansyncGeneratorFn<Return, Args> | Promise<Return>, options?: QuansyncOptions): QuansyncFn<Return, Args>; | ||
| /** | ||
@@ -17,3 +28,8 @@ * Converts a promise to a Quansync generator. | ||
| declare function toGenerator<T>(promise: Promise<T> | QuansyncGenerator<T> | T): QuansyncGenerator<T>; | ||
| /** | ||
| * @returns `true` if the current context is async, `false` otherwise. | ||
| */ | ||
| declare function getIsAsync(): Generator<typeof GET_IS_ASYNC, boolean, unknown>; | ||
| export { GET_IS_ASYNC, QuansyncError, QuansyncFn, QuansyncGenerator, QuansyncInput, quansync, toGenerator }; | ||
| export { GET_IS_ASYNC, QuansyncError, getIsAsync, quansync, toGenerator }; | ||
| export type { QuansyncFn, QuansyncGenerator, QuansyncGeneratorFn, QuansyncInputObject, QuansyncOptions }; |
+20
-4
@@ -1,4 +0,14 @@ | ||
| import { QuansyncInput, QuansyncFn, QuansyncGenerator } from './types.js'; | ||
| export { QuansyncAwaitableGenerator, QuansyncGeneratorFn, QuansyncInputObject } from './types.js'; | ||
| import type { QuansyncFn, QuansyncGenerator, QuansyncGeneratorFn, QuansyncInputObject, QuansyncOptions } from './types.js'; | ||
| export { QuansyncAwaitableGenerator, QuansyncInput } from './types.js'; | ||
| declare const GET_IS_ASYNC: unique symbol; | ||
@@ -11,3 +21,4 @@ declare class QuansyncError extends Error { | ||
| */ | ||
| declare function quansync<Return, Args extends any[] = []>(options: QuansyncInput<Return, Args> | Promise<Return>): QuansyncFn<Return, Args>; | ||
| declare function quansync<Return, Args extends any[] = []>(input: QuansyncInputObject<Return, Args>): QuansyncFn<Return, Args>; | ||
| declare function quansync<Return, Args extends any[] = []>(input: QuansyncGeneratorFn<Return, Args> | Promise<Return>, options?: QuansyncOptions): QuansyncFn<Return, Args>; | ||
| /** | ||
@@ -17,3 +28,8 @@ * Converts a promise to a Quansync generator. | ||
| declare function toGenerator<T>(promise: Promise<T> | QuansyncGenerator<T> | T): QuansyncGenerator<T>; | ||
| /** | ||
| * @returns `true` if the current context is async, `false` otherwise. | ||
| */ | ||
| declare function getIsAsync(): Generator<typeof GET_IS_ASYNC, boolean, unknown>; | ||
| export { GET_IS_ASYNC, QuansyncError, QuansyncFn, QuansyncGenerator, QuansyncInput, quansync, toGenerator }; | ||
| export { GET_IS_ASYNC, QuansyncError, getIsAsync, quansync, toGenerator }; | ||
| export type { QuansyncFn, QuansyncGenerator, QuansyncGeneratorFn, QuansyncInputObject, QuansyncOptions }; |
+18
-14
@@ -50,7 +50,8 @@ const GET_IS_ASYNC = Symbol.for("quansync.getIsAsync"); | ||
| } | ||
| function iterateSync(generator) { | ||
| const DEFAULT_ON_YIELD = (value) => value; | ||
| function iterateSync(generator, onYield = DEFAULT_ON_YIELD) { | ||
| let current = generator.next(); | ||
| while (!current.done) { | ||
| try { | ||
| current = generator.next(unwrapYield(current.value)); | ||
| current = generator.next(unwrapYield(onYield(current.value, false))); | ||
| } catch (err) { | ||
@@ -62,7 +63,7 @@ current = generator.throw(err); | ||
| } | ||
| async function iterateAsync(generator) { | ||
| async function iterateAsync(generator, onYield = DEFAULT_ON_YIELD) { | ||
| let current = generator.next(); | ||
| while (!current.done) { | ||
| try { | ||
| current = generator.next(await unwrapYield(current.value, true)); | ||
| current = generator.next(await unwrapYield(onYield(current.value, true), true)); | ||
| } catch (err) { | ||
@@ -74,20 +75,20 @@ current = generator.throw(err); | ||
| } | ||
| function fromGeneratorFn(generatorFn) { | ||
| function fromGeneratorFn(generatorFn, options) { | ||
| return fromObject({ | ||
| name: generatorFn.name, | ||
| async(...args) { | ||
| return iterateAsync(generatorFn.apply(this, args)); | ||
| return iterateAsync(generatorFn.apply(this, args), options?.onYield); | ||
| }, | ||
| sync(...args) { | ||
| return iterateSync(generatorFn.apply(this, args)); | ||
| return iterateSync(generatorFn.apply(this, args), options?.onYield); | ||
| } | ||
| }); | ||
| } | ||
| function quansync(options) { | ||
| if (isThenable(options)) | ||
| return fromPromise(options); | ||
| if (typeof options === "function") | ||
| return fromGeneratorFn(options); | ||
| function quansync(input, options) { | ||
| if (isThenable(input)) | ||
| return fromPromise(input); | ||
| if (typeof input === "function") | ||
| return fromGeneratorFn(input, options); | ||
| else | ||
| return fromObject(options); | ||
| return fromObject(input); | ||
| } | ||
@@ -99,3 +100,6 @@ function toGenerator(promise) { | ||
| } | ||
| function* getIsAsync() { | ||
| return !!(yield GET_IS_ASYNC); | ||
| } | ||
| export { GET_IS_ASYNC, QuansyncError, quansync, toGenerator }; | ||
| export { GET_IS_ASYNC, QuansyncError, getIsAsync, quansync, toGenerator }; |
+16
-4
@@ -1,4 +0,12 @@ | ||
| import { QuansyncInput, QuansyncFn } from './types.cjs'; | ||
| export { QuansyncAwaitableGenerator, QuansyncGenerator, QuansyncGeneratorFn, QuansyncInputObject } from './types.cjs'; | ||
| import type { QuansyncFn, QuansyncGeneratorFn, QuansyncInputObject, QuansyncOptions } from './types.cjs'; | ||
| export { QuansyncAwaitableGenerator, QuansyncGenerator, QuansyncInput } from './types.cjs'; | ||
| /** | ||
@@ -13,4 +21,8 @@ * This function is equivalent to `quansync` from main entry | ||
| */ | ||
| declare const quansync: <Return, Args extends any[] = []>(options: QuansyncInput<Return, Args> | ((...args: Args) => Promise<Return> | Return)) => QuansyncFn<Return, Args>; | ||
| declare const quansync: { | ||
| <Return, Args extends any[] = []>(input: QuansyncInputObject<Return, Args>): QuansyncFn<Return, Args>; | ||
| <Return, Args extends any[] = []>(input: QuansyncGeneratorFn<Return, Args> | Promise<Return> | ((...args: Args) => Promise<Return> | Return), options?: QuansyncOptions): QuansyncFn<Return, Args>; | ||
| }; | ||
| export { QuansyncFn, QuansyncInput, quansync }; | ||
| export { quansync }; | ||
| export type { QuansyncFn, QuansyncGeneratorFn, QuansyncInputObject, QuansyncOptions }; |
+16
-4
@@ -1,4 +0,12 @@ | ||
| import { QuansyncInput, QuansyncFn } from './types.mjs'; | ||
| export { QuansyncAwaitableGenerator, QuansyncGenerator, QuansyncGeneratorFn, QuansyncInputObject } from './types.mjs'; | ||
| import type { QuansyncFn, QuansyncGeneratorFn, QuansyncInputObject, QuansyncOptions } from './types.mjs'; | ||
| export { QuansyncAwaitableGenerator, QuansyncGenerator, QuansyncInput } from './types.mjs'; | ||
| /** | ||
@@ -13,4 +21,8 @@ * This function is equivalent to `quansync` from main entry | ||
| */ | ||
| declare const quansync: <Return, Args extends any[] = []>(options: QuansyncInput<Return, Args> | ((...args: Args) => Promise<Return> | Return)) => QuansyncFn<Return, Args>; | ||
| declare const quansync: { | ||
| <Return, Args extends any[] = []>(input: QuansyncInputObject<Return, Args>): QuansyncFn<Return, Args>; | ||
| <Return, Args extends any[] = []>(input: QuansyncGeneratorFn<Return, Args> | Promise<Return> | ((...args: Args) => Promise<Return> | Return), options?: QuansyncOptions): QuansyncFn<Return, Args>; | ||
| }; | ||
| export { QuansyncFn, QuansyncInput, quansync }; | ||
| export { quansync }; | ||
| export type { QuansyncFn, QuansyncGeneratorFn, QuansyncInputObject, QuansyncOptions }; |
+16
-4
@@ -1,4 +0,12 @@ | ||
| import { QuansyncInput, QuansyncFn } from './types.js'; | ||
| export { QuansyncAwaitableGenerator, QuansyncGenerator, QuansyncGeneratorFn, QuansyncInputObject } from './types.js'; | ||
| import type { QuansyncFn, QuansyncGeneratorFn, QuansyncInputObject, QuansyncOptions } from './types.js'; | ||
| export { QuansyncAwaitableGenerator, QuansyncGenerator, QuansyncInput } from './types.js'; | ||
| /** | ||
@@ -13,4 +21,8 @@ * This function is equivalent to `quansync` from main entry | ||
| */ | ||
| declare const quansync: <Return, Args extends any[] = []>(options: QuansyncInput<Return, Args> | ((...args: Args) => Promise<Return> | Return)) => QuansyncFn<Return, Args>; | ||
| declare const quansync: { | ||
| <Return, Args extends any[] = []>(input: QuansyncInputObject<Return, Args>): QuansyncFn<Return, Args>; | ||
| <Return, Args extends any[] = []>(input: QuansyncGeneratorFn<Return, Args> | Promise<Return> | ((...args: Args) => Promise<Return> | Return), options?: QuansyncOptions): QuansyncFn<Return, Args>; | ||
| }; | ||
| export { QuansyncFn, QuansyncInput, quansync }; | ||
| export { quansync }; | ||
| export type { QuansyncFn, QuansyncGeneratorFn, QuansyncInputObject, QuansyncOptions }; |
+5
-2
@@ -1,2 +0,5 @@ | ||
| interface QuansyncInputObject<Return, Args extends any[]> { | ||
| interface QuansyncOptions { | ||
| onYield?: (value: any, isAsync: boolean) => any; | ||
| } | ||
| interface QuansyncInputObject<Return, Args extends any[]> extends QuansyncOptions { | ||
| name?: string; | ||
@@ -20,2 +23,2 @@ sync: (...args: Args) => Return; | ||
| export type { QuansyncAwaitableGenerator, QuansyncFn, QuansyncGenerator, QuansyncGeneratorFn, QuansyncInput, QuansyncInputObject }; | ||
| export type { QuansyncAwaitableGenerator, QuansyncFn, QuansyncGenerator, QuansyncGeneratorFn, QuansyncInput, QuansyncInputObject, QuansyncOptions }; |
+5
-2
@@ -1,2 +0,5 @@ | ||
| interface QuansyncInputObject<Return, Args extends any[]> { | ||
| interface QuansyncOptions { | ||
| onYield?: (value: any, isAsync: boolean) => any; | ||
| } | ||
| interface QuansyncInputObject<Return, Args extends any[]> extends QuansyncOptions { | ||
| name?: string; | ||
@@ -20,2 +23,2 @@ sync: (...args: Args) => Return; | ||
| export type { QuansyncAwaitableGenerator, QuansyncFn, QuansyncGenerator, QuansyncGeneratorFn, QuansyncInput, QuansyncInputObject }; | ||
| export type { QuansyncAwaitableGenerator, QuansyncFn, QuansyncGenerator, QuansyncGeneratorFn, QuansyncInput, QuansyncInputObject, QuansyncOptions }; |
+5
-2
@@ -1,2 +0,5 @@ | ||
| interface QuansyncInputObject<Return, Args extends any[]> { | ||
| interface QuansyncOptions { | ||
| onYield?: (value: any, isAsync: boolean) => any; | ||
| } | ||
| interface QuansyncInputObject<Return, Args extends any[]> extends QuansyncOptions { | ||
| name?: string; | ||
@@ -20,2 +23,2 @@ sync: (...args: Args) => Return; | ||
| export type { QuansyncAwaitableGenerator, QuansyncFn, QuansyncGenerator, QuansyncGeneratorFn, QuansyncInput, QuansyncInputObject }; | ||
| export type { QuansyncAwaitableGenerator, QuansyncFn, QuansyncGenerator, QuansyncGeneratorFn, QuansyncInput, QuansyncInputObject, QuansyncOptions }; |
+8
-8
| { | ||
| "name": "quansync", | ||
| "type": "module", | ||
| "version": "0.2.8", | ||
| "version": "0.2.9", | ||
| "description": "Create sync/async APIs with usable logic", | ||
@@ -65,8 +65,8 @@ "author": "Anthony Fu <anthonyfu117@hotmail.com>", | ||
| "devDependencies": { | ||
| "@antfu/eslint-config": "^4.4.0", | ||
| "@types/node": "^22.13.8", | ||
| "bumpp": "^10.0.3", | ||
| "eslint": "^9.21.0", | ||
| "@antfu/eslint-config": "^4.10.1", | ||
| "@types/node": "^22.13.10", | ||
| "bumpp": "^10.1.0", | ||
| "eslint": "^9.22.0", | ||
| "gensync": "1.0.0-beta.2", | ||
| "lint-staged": "^15.4.3", | ||
| "lint-staged": "^15.5.0", | ||
| "mitata": "^1.0.34", | ||
@@ -77,4 +77,4 @@ "simple-git-hooks": "^2.11.1", | ||
| "unbuild": "^3.5.0", | ||
| "vite": "^6.2.0", | ||
| "vitest": "^3.0.7" | ||
| "vite": "^6.2.2", | ||
| "vitest": "^3.0.9" | ||
| }, | ||
@@ -81,0 +81,0 @@ "simple-git-hooks": { |
+26
-13
@@ -19,2 +19,6 @@ # quansync | ||
| ## Why & How | ||
| Please refer to Anthony's blog post: [**Async, Sync, in Between**](https://antfu.me/posts/async-sync-in-between). | ||
| ## Usage | ||
@@ -51,21 +55,19 @@ | ||
| ## Why | ||
| ### `getIsAsync` | ||
| // TODO: | ||
| Returns a boolean indicating whether the current execution is in async mode. | ||
| ## How it works | ||
| ```ts | ||
| import { getIsAsync, quansync } from 'quansync' | ||
| // TODO: | ||
| const fn = quansync(function* () { | ||
| const isAsync: boolean = yield* getIsAsync() | ||
| console.log(isAsync) | ||
| }) | ||
| ## Benchmark | ||
| Run the following command to benchmark the performance of `quansync`: | ||
| ```bash | ||
| pnpm run build && pnpm run benchmark | ||
| fn.sync() // false | ||
| await fn() // true | ||
| await fn.async() // true | ||
| ``` | ||
| Benchmark results indicate that each `yield` incurs an overhead of | ||
| approximately 150 ns, comparable to that of `await sync()`. (On Apple M1 Max) | ||
| ## Build-time Macro | ||
@@ -104,2 +106,13 @@ | ||
| ## Benchmark | ||
| Run the following command to benchmark the performance of `quansync`: | ||
| ```bash | ||
| pnpm run build && pnpm run benchmark | ||
| ``` | ||
| Benchmark results indicate that each `yield` incurs an overhead of | ||
| approximately 150 ns, comparable to that of `await sync()`. (On Apple M1 Max) | ||
| ## Sponsors | ||
@@ -106,0 +119,0 @@ |
23906
16.25%273
8.76%145
9.85%