Skip to content

Commit f3eda8e

Browse files
authored
feat: add onYield hook (#16)
1 parent 827de2e commit f3eda8e

File tree

3 files changed

+62
-15
lines changed

3 files changed

+62
-15
lines changed

src/index.ts

+30-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { QuansyncAwaitableGenerator, QuansyncFn, QuansyncGenerator, QuansyncGeneratorFn, QuansyncInput, QuansyncInputObject } from './types'
1+
import type { QuansyncAwaitableGenerator, QuansyncFn, QuansyncGenerator, QuansyncGeneratorFn, QuansyncInput, QuansyncInputObject, QuansyncOptions } from './types'
22

33
export * from './types'
44

@@ -59,11 +59,16 @@ function unwrapYield(value: any, isAsync?: boolean): any {
5959
return value
6060
}
6161

62-
function iterateSync<Return>(generator: QuansyncGenerator<Return, unknown>): Return {
62+
const DEFAULT_ON_YIELD = (value: any): any => value
63+
64+
function iterateSync<Return>(
65+
generator: QuansyncGenerator<Return, unknown>,
66+
onYield: QuansyncOptions['onYield'] = DEFAULT_ON_YIELD,
67+
): Return {
6368
let current = generator.next()
6469
while (!current.done) {
6570
try {
66-
current = generator.next(unwrapYield(current.value))
71+
current = generator.next(unwrapYield(onYield(current.value, false)))
6772
}
6873
catch (err) {
6974
current = generator.throw(err)
@@ -72,11 +77,14 @@ function iterateSync<Return>(generator: QuansyncGenerator<Return, unknown>): Ret
7277
return unwrapYield(current.value)
7378
}
7479

75-
async function iterateAsync<Return>(generator: QuansyncGenerator<Return, unknown>): Promise<Return> {
80+
async function iterateAsync<Return>(
81+
generator: QuansyncGenerator<Return, unknown>,
82+
onYield: QuansyncOptions['onYield'] = DEFAULT_ON_YIELD,
83+
): Promise<Return> {
7684
let current = generator.next()
7785
while (!current.done) {
7886
try {
79-
current = generator.next(await unwrapYield(current.value, true))
87+
current = generator.next(await unwrapYield(onYield(current.value, true), true))
8088
}
8189
catch (err) {
8290
current = generator.throw(err)
@@ -87,14 +95,15 @@ async function iterateAsync<Return>(generator: QuansyncGenerator<Return, unknown
8795

8896
function fromGeneratorFn<Return, Args extends any[]>(
8997
generatorFn: QuansyncGeneratorFn<Return, Args>,
98+
options?: QuansyncOptions,
9099
): QuansyncFn<Return, Args> {
91100
return fromObject({
92101
name: generatorFn.name,
93102
async(...args) {
94-
return iterateAsync(generatorFn.apply(this, args))
103+
return iterateAsync(generatorFn.apply(this, args), options?.onYield)
95104
},
96105
sync(...args) {
97-
return iterateSync(generatorFn.apply(this, args))
106+
return iterateSync(generatorFn.apply(this, args), options?.onYield)
98107
},
99108
})
100109
}
@@ -103,14 +112,22 @@ function fromGeneratorFn<Return, Args extends any[]>(
103112
* Creates a new Quansync function, a "superposition" between async and sync.
104113
*/
105114
export function quansync<Return, Args extends any[] = []>(
106-
options: QuansyncInput<Return, Args> | Promise<Return>,
115+
input: QuansyncInputObject<Return, Args>,
116+
): QuansyncFn<Return, Args>
117+
export function quansync<Return, Args extends any[] = []>(
118+
input: QuansyncGeneratorFn<Return, Args> | Promise<Return>,
119+
options?: QuansyncOptions,
120+
): QuansyncFn<Return, Args>
121+
export function quansync<Return, Args extends any[] = []>(
122+
input: QuansyncInput<Return, Args> | Promise<Return>,
123+
options?: QuansyncOptions,
107124
): QuansyncFn<Return, Args> {
108-
if (isThenable(options))
109-
return fromPromise<Return>(options)
110-
if (typeof options === 'function')
111-
return fromGeneratorFn(options)
125+
if (isThenable(input))
126+
return fromPromise<Return>(input)
127+
if (typeof input === 'function')
128+
return fromGeneratorFn(input, options)
112129
else
113-
return fromObject(options)
130+
return fromObject(input)
114131
}
115132

116133
/**

src/types.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
export interface QuansyncInputObject<Return, Args extends any[]> {
1+
export interface QuansyncOptions {
2+
onYield?: (value: any, isAsync: boolean) => any
3+
}
4+
5+
export interface QuansyncInputObject<Return, Args extends any[]> extends QuansyncOptions {
26
name?: string
37
sync: (...args: Args) => Return
48
async: (...args: Args) => Promise<Return>

test/index.test.ts

+27-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { expect, it } from 'vitest'
1+
import { expect, it, vi } from 'vitest'
22
import { quansync, toGenerator } from '../src'
33
import { quansync as quansyncMacro } from '../src/macro'
44

@@ -256,3 +256,29 @@ it('bind this', async () => {
256256
await expect(cls.async()).resolves.instanceOf(Cls)
257257
expect(cls.sync()).instanceOf(Cls)
258258
})
259+
260+
it('call onYield hook', async () => {
261+
const onYield = vi.fn(v => v + 1)
262+
const run = quansync(function* () {
263+
expect(yield 1).toBe(2)
264+
expect(yield 2).toBe(3)
265+
}, { onYield })
266+
267+
await run.async()
268+
expect(onYield).toBeCalledTimes(2)
269+
run.sync()
270+
expect(onYield).toBeCalledTimes(4)
271+
272+
// custom error on promise
273+
const run2 = quansync(function* () {
274+
return yield Promise.resolve('foo')
275+
}, {
276+
onYield: (v, isAsync) => {
277+
if (!isAsync && v instanceof Promise)
278+
throw new TypeError('custom error')
279+
return v
280+
},
281+
})
282+
expect(() => run2.sync()).toThrowErrorMatchingInlineSnapshot(`[TypeError: custom error]`)
283+
await expect(run2.async()).resolves.toMatchInlineSnapshot(`"foo"`)
284+
})

0 commit comments

Comments
 (0)