core icon indicating copy to clipboard operation
core copied to clipboard

feat(runtime-core): return result of handlers in `emit`

Open jaulz opened this issue 3 years ago • 6 comments

It would be great if emit could return the result of the handlers. A typical example could be to show a loading indicator while asynchronous event handlers are running:

const loading = ref(false)

const handleClick = async () => {
  loading.value = true
  await Promise.all(emit('click'))
  loading.value = false
}

This is just a basic example that does not consider errors or multiple clicks but it gives an idea what would be possible. The same could already be implemented with props but it duplicates the handling.

If you think this is a good idea, I can extend the PR with corresponding tests.

jaulz avatar Oct 04 '22 12:10 jaulz

I'm not sure if we should have an issue discussing this feature first.

Yep, for sure, I thought this PR could serve as an initial base to discuss it further. Happy to move it somewhere else if you want?

jaulz avatar Oct 05 '22 04:10 jaulz

Deploy Preview for vuejs-coverage failed.

Name Link
Latest commit a1cac63b1cc39f87733b0eea45ee259412af8373
Latest deploy log https://app.netlify.com/sites/vuejs-coverage/deploys/633d2b62989f1b000874005a

netlify[bot] avatar Oct 05 '22 07:10 netlify[bot]

It's an underrated feature here! First of all, it would really be nice to have:

<!-- Outside -->
<SomeFormComponent
	@validate="validateData"
	@submit="saveDataToDatabase"
/>
<script>
async function validateData() {
	await API.validateXXX()
}
async function saveDataToDatabase() {
	await API.postXXX()
}
</script>
<!-- Inside -->
<form />
<script>
const emit = defineEmits()
async function handleClickSubmit() {
	startLoading()
	await emit('validate', data)
	await emit('submit', data)
	stopLoading()
}
</script>

Nice and clean, right? But for now, we can't write in this way. Because there is actually an untold rule on event handlers.

defineEmits<{
	(e: 'submit'): Promise<undefined> // ❌
	(e: 'submit'): void // ✅
}>()

The return type can only be void. You won't get the result returned by your event handlers. The whole EMIT thing is basically an EventEmitter, and in a typical EventEmitter model, one event key can have multiple handlers. Despite the fact that @sxzz has looked up the code, and handler won't be an array. So, Vue should make a decision on its Emit model. Either Vue supports Returning result of event handlers or stress the void type in doc like:

defineEmits<{
	(e: 'aaa'): void
	(e: 'bbb'): void
	/** .... */
}>()

newVincentFong avatar Nov 10 '22 10:11 newVincentFong

I realy need this feature, is this feature likely to be merged? when will it be available?

mrchar avatar May 08 '23 03:05 mrchar

There's an RFC discussion at https://github.com/vuejs/rfcs/discussions/587 that proposes a slightly different way of approaching this. I'm not sure which I prefer.


Just to clarify, a single event can have multiple listeners. In the Playground below, the emitted event triggers 3 handlers. mergeProps ensures that these are combined into an array.

emit will need to return an array in all cases, it can't just assume a single listener. The RFC above seems to be suggesting that the array should be automatically wrapped in a promise, whereas the approach here seem to leave it to the user to call Promise.all.

skirtles-code avatar Apr 24 '24 08:04 skirtles-code