Skip to content

Commit e81c8a3

Browse files
committed
feat(runtime-core): emit now returns array of return values from all triggered handlers
close #635
1 parent aca2c2a commit e81c8a3

File tree

4 files changed

+124
-8
lines changed

4 files changed

+124
-8
lines changed

packages/runtime-core/__tests__/component.spec.ts

+39-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { h, ref, render, nodeOps, nextTick } from '@vue/runtime-test'
1+
import {
2+
h,
3+
ref,
4+
render,
5+
nodeOps,
6+
nextTick,
7+
defineComponent
8+
} from '@vue/runtime-test'
29

310
describe('renderer: component', () => {
411
test.todo('should work')
@@ -52,4 +59,35 @@ describe('renderer: component', () => {
5259
expect(spy).toHaveBeenCalledTimes(2)
5360
})
5461
})
62+
63+
test('emit', async () => {
64+
let noMatchEmitResult: any
65+
let singleEmitResult: any
66+
let multiEmitResult: any
67+
68+
const Child = defineComponent({
69+
setup(_, { emit }) {
70+
noMatchEmitResult = emit('foo')
71+
singleEmitResult = emit('bar')
72+
multiEmitResult = emit('baz')
73+
return () => h('div')
74+
}
75+
})
76+
77+
const App = {
78+
setup() {
79+
return () =>
80+
h(Child, {
81+
onBar: () => 1,
82+
onBaz: [() => Promise.resolve(2), () => Promise.resolve(3)]
83+
})
84+
}
85+
}
86+
87+
render(h(App), nodeOps.createElement('div'))
88+
89+
expect(noMatchEmitResult).toMatchObject([])
90+
expect(singleEmitResult).toMatchObject([1])
91+
expect(await Promise.all(multiEmitResult)).toMatchObject([2, 3])
92+
})
5593
})

packages/runtime-core/__tests__/errorHandling.spec.ts

+73-1
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ describe('error handling', () => {
362362
expect(fn).toHaveBeenCalledWith(err, 'watcher cleanup function')
363363
})
364364

365-
test('in component event handler', () => {
365+
test('in component event handler via emit', () => {
366366
const err = new Error('foo')
367367
const fn = jest.fn()
368368

@@ -392,6 +392,78 @@ describe('error handling', () => {
392392
expect(fn).toHaveBeenCalledWith(err, 'component event handler')
393393
})
394394

395+
test('in component event handler via emit (async)', async () => {
396+
const err = new Error('foo')
397+
const fn = jest.fn()
398+
399+
const Comp = {
400+
setup() {
401+
onErrorCaptured((err, instance, info) => {
402+
fn(err, info)
403+
return true
404+
})
405+
return () =>
406+
h(Child, {
407+
async onFoo() {
408+
throw err
409+
}
410+
})
411+
}
412+
}
413+
414+
let res: any
415+
const Child = {
416+
setup(props: any, { emit }: any) {
417+
res = emit('foo')
418+
return () => null
419+
}
420+
}
421+
422+
render(h(Comp), nodeOps.createElement('div'))
423+
424+
try {
425+
await Promise.all(res)
426+
} catch (e) {
427+
expect(e).toBe(err)
428+
}
429+
expect(fn).toHaveBeenCalledWith(err, 'component event handler')
430+
})
431+
432+
test('in component event handler via emit (async + array)', async () => {
433+
const err = new Error('foo')
434+
const fn = jest.fn()
435+
436+
const Comp = {
437+
setup() {
438+
onErrorCaptured((err, instance, info) => {
439+
fn(err, info)
440+
return true
441+
})
442+
return () =>
443+
h(Child, {
444+
onFoo: [() => Promise.reject(err), () => Promise.resolve(1)]
445+
})
446+
}
447+
}
448+
449+
let res: any
450+
const Child = {
451+
setup(props: any, { emit }: any) {
452+
res = emit('foo')
453+
return () => null
454+
}
455+
}
456+
457+
render(h(Comp), nodeOps.createElement('div'))
458+
459+
try {
460+
await Promise.all(res)
461+
} catch (e) {
462+
expect(e).toBe(err)
463+
}
464+
expect(fn).toHaveBeenCalledWith(err, 'component event handler')
465+
})
466+
395467
it('should warn unhandled', () => {
396468
const onError = jest.spyOn(console, 'error')
397469
onError.mockImplementation(() => {})

packages/runtime-core/src/component.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import {
2424
isObject,
2525
NO,
2626
makeMap,
27-
isPromise
27+
isPromise,
28+
isArray
2829
} from '@vue/shared'
2930
import { SuspenseBoundary } from './components/Suspense'
3031
import { CompilerOptions } from '@vue/compiler-core'
@@ -70,7 +71,7 @@ export const enum LifecycleHooks {
7071
ERROR_CAPTURED = 'ec'
7172
}
7273

73-
export type Emit = (event: string, ...args: unknown[]) => void
74+
export type Emit = (event: string, ...args: unknown[]) => any[]
7475

7576
export interface SetupContext {
7677
attrs: Data
@@ -218,16 +219,19 @@ export function defineComponentInstance(
218219
rtc: null,
219220
ec: null,
220221

221-
emit: (event, ...args) => {
222+
emit: (event, ...args): any[] => {
222223
const props = instance.vnode.props || EMPTY_OBJ
223224
const handler = props[`on${event}`] || props[`on${capitalize(event)}`]
224225
if (handler) {
225-
callWithAsyncErrorHandling(
226+
const res = callWithAsyncErrorHandling(
226227
handler,
227228
instance,
228229
ErrorCodes.COMPONENT_EVENT_HANDLER,
229230
args
230231
)
232+
return isArray(res) ? res : [res]
233+
} else {
234+
return []
231235
}
232236
}
233237
}

packages/runtime-core/src/errorHandling.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export function callWithAsyncErrorHandling(
7474
instance: ComponentInternalInstance | null,
7575
type: ErrorTypes,
7676
args?: unknown[]
77-
) {
77+
): any[] {
7878
if (isFunction(fn)) {
7979
const res = callWithErrorHandling(fn, instance, type, args)
8080
if (res != null && !res._isVue && isPromise(res)) {
@@ -85,9 +85,11 @@ export function callWithAsyncErrorHandling(
8585
return res
8686
}
8787

88+
const values = []
8889
for (let i = 0; i < fn.length; i++) {
89-
callWithAsyncErrorHandling(fn[i], instance, type, args)
90+
values.push(callWithAsyncErrorHandling(fn[i], instance, type, args))
9091
}
92+
return values
9193
}
9294

9395
export function handleError(

0 commit comments

Comments
 (0)