Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return a Promise from vm.$once() with callback omitted #8444

Closed
loilo opened this issue Jul 2, 2018 · 7 comments
Closed

Return a Promise from vm.$once() with callback omitted #8444

loilo opened this issue Jul 2, 2018 · 7 comments

Comments

@loilo
Copy link

loilo commented Jul 2, 2018

What problem does this feature solve?

Deferring a process to wait for a signal currently is a little bit tedious. Signals are usually carried as events and reacted to via vm.$on() resp. vm.$once().
Now that we have async/await, we could improve developer ergonomy greatly by having a Promise returned from vm.$once() – similar to vm.$nextTick().

What does the proposed API look like?

Note: I've omitted any additional boilerplate from the examples below. Please assume each example is executed in the context of an async Vue method.

So what we'd want to do is deferring the execution of our code until the signal event is emitted by our Vue instance.

A quick recap how this could currently be done:

// Old School: Use a callback
this.$once('signal', (...payload) => {
  // Could lead to some callback hell though
})

// Alternatively: Wrap in a Promise
const payload = await new Promise(resolve =>
  this.$once('signal', (...payload) => resolve(payload))
)
// However, this is an awful lot of boilerplate, and it gets incredibly messy if we'd want to await various different signals

How this would look with the proposed change:

// The following $once() call got no callback parameter, so it returns a Promise (which resolves to an array of the event parameters)
const payload = await this.$once('signal')

// This is effectively equivalent to the Promise wrap example from above

// Bonus: Multiple different signals
const payloads = await Promise.all([
  this.$once('signal-1'),
  this.$once('signal-2'),
  this.$once('signal-3')
])

Note that a Promise would only be returned if the callback parameter was omitted, so the following still works:

// Inside some method
this
  .$once('signal', (...payload) => {
    // This $once() call still returns the Vue instance, no change to current behaviour
  })
  .$on('other-event', () => {
    // That means we can still chain event listeners and avoid a practical breaking change
  })

While this is technically a breaking change, it's a rather soft one since previously using the $once() method without a callback had no use case.

To me, this change sounds like pretty low-hanging fruit. I'd love to hear your opinion regarding this idea.

@loilo loilo changed the title Return a Promise on vm.$once with callback omitted Return a Promise from vm.$once() with callback omitted Jul 2, 2018
@posva
Copy link
Member

posva commented Jul 2, 2018

Isn't this doable in userland (non tested):

const { $once } = Vue.prototype
Vue.prototype.$once = function once (event, cb) {
	if (cb == null) return new Promise(resolve => $once.call(this, resolve))
	$once.call(this, cb)
}

@loilo
Copy link
Author

loilo commented Jul 2, 2018

This in fact works, but I wouldn't feel comfortable at all to modify built-in functionality like that to be honest.

@posva
Copy link
Member

posva commented Jul 2, 2018

Would you be more comfortable if I hide it behind a plugin 😄 ?

@loilo
Copy link
Author

loilo commented Jul 2, 2018

Only if you didn't tell me and I didn't know. 🙈 Which is pretty contrary to using it though. 😁

@loilo
Copy link
Author

loilo commented Jul 2, 2018

FWIW, the change to vm.$nextTick() could've been done in userland in the same way. 🙊

@posva
Copy link
Member

posva commented Jul 2, 2018

It was only half a joke though.

nextTick does fit a Promise because the callback is async code. $once callback is a function called synchronously when the event is triggered. There is nothing sure about the callback being triggered.
Using a promise makes some code very succinct to write in that very specific scenario, but the usage is still quite debatable, that's why I think this should be kept in userland

@posva posva closed this as completed Jul 2, 2018
@loilo
Copy link
Author

loilo commented Jul 2, 2018

I'd like to humbly disagree.

While $once is called synchronously, its event's origin is usually of asynchronous nature (via a user interaction). However I don't even think Promises should be specific to async code. I'd say they are just as much about flow control and ergonomics, which makes them useful in pretty much all places where a one-time callback would be suitable.

The point about not knowing if the Promise will ever resolve makes me feel uncomfortable as well, but I don't think that's a very rational objection. It's not really different from a $once callback never being called.

However, I think there's no obligation for us to agree on that topic. Thanks for discussing this issue with me. 🙂

P.S.: FWIW, the Node.js team currently has basically the same discussion going on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants