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

feat(core): add 'Vue.prototype.$notify' #7465

Closed
wants to merge 1 commit into from
Closed

Conversation

CarterLi
Copy link

@CarterLi CarterLi commented Jan 17, 2018

What kind of change does this PR introduce? (check at least one)

  • Bugfix
  • Feature
  • Code style update
  • Refactor
  • Build-related changes
  • Other, please describe:

Does this PR introduce a breaking change? (check one)

  • Yes
  • No

If yes, please describe the impact and migration path for existing applications:

The PR fulfills these requirements:

If adding a new feature, the PR's description includes:

  • A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it)

Other information:

This PR adds a new function Vue.prototype.$notify, which works basically like $emit, except it returns an array that collects all return values of its event callbacks.

Motivation:

Developers of an component may want to collect data from callbacks of an event. There are a few options to do this:

  1. Bind a function directly into the component. But it forces users of the component to write a function or needs the creator of the component writes property type checks to determine whether the prop value is callable or not.
  2. Pass a callback parameter for the event. But nobody wants callbacks today.

To solve this issue, I'd like $emit or something else returns an array that collects all return values of its event callbacks. In order not to break old code, the behavior of $emit was kept ( which returns this ), but I wrote a new function instead. The function name $notify is not decided, but I can't come up with a better name.

Related issue: #5443
Related question: https://segmentfault.com/q/1010000011143372

@sirlancelot
Copy link

sirlancelot commented Jan 17, 2018

I'm not a huge fan of this because it is impossible to know the order in which the callbacks get executed.

When I encounter a situation where I want to get something back from an $emit, I usually pass a callback or a deferred as part of the arguments like so:

// callback can be called multiple times by different consumers...
this.$emit("get-something", arg, (value) => {
    // Do stuff with value
})

Use it like so:

this.$on("get-something", (arg, done) => {
    // Send a value back to the emitting component
    if (done) done("hello from consumer")
})

This manner creates a more predictable outcome where the developer decides whether to send a value back. It also allows for an alternative situation where you can only listen for a single value using a deferred like so:

var callback
const deferred = new Promise((resolve) => { callback = resolve })

// callback will only save the value of the first caller
this.$emit("get-something", arg, callback)

deferred.then((value) => {
    // Do something with resolved value
})

@yyx990803
Copy link
Member

Thanks for the PR - however, I agree w/ @sirlancelot and I think explicit callbacks is more straightforward.

@yyx990803 yyx990803 closed this Jan 17, 2018
@CarterLi
Copy link
Author

CarterLi commented Jan 18, 2018

It's 2018. I don't think callbacks are a good design anymore.

What I want to write is:

const values = await Promise.all(vm.$emit('event', arg))

Instead of

var promises = []
vm.$emit('event', arg, promise => {
  promises.push(promise)
})
const values = await Promise.all(promises)

returning vm itself for $emit is almost useless. It's not jQuery or lodash and you don't need to chain the operations in 99% cases (there's no UT to ensure this by the way). But it's a common use case that returning values from event handler, for example filtering list remotely. Using return value instead of vender callbacks is much easier to use and a rather modern design nowadays.

I'm not a huge fan of this because it is impossible to know the order in which the callbacks get executed.

Values in the returned array of $notify are still ordered. Besides I think most of the use cases is @event="handler" thus no order is needed.

What you get using callback is just the callback is called BEFORE executing the next handler, but you get no benefit from this. You can't stop the event bus ( by throwing exceptions or something else ). If you want to return something from the callback, it must be a design error.

If you do, just fall back to callbacks.

@yyx990803 @sirlancelot any thoughts?

@yyx990803
Copy link
Member

Well... It's 2018. I don't think event-ping-ponging is a good design anymore. It makes the flow of your application very hard to reason about for those who are not familiar with the codebase. I don't want to encourage this kind of patterns. If you insist, you don't have to get this into Vue core to use it. Just write your own plugin.

@CarterLi
Copy link
Author

CarterLi commented Jan 18, 2018

It makes the flow of your application very hard to reason about for those who are not familiar with the codebase.

I agree. But it's not avoidable. There are lots of similar real requirements need to be resolved.

remote-method | 远程搜索方法 | function | — | —
before-upload | 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。 | function(file) | — | —
before-remove | 删除文件之前的钩子,参数为上传的文件和文件列表,若返回 false 或者返回 Promise 且被 reject,则停止上传。 | function(file, fileList) | — | —
...

The element-ui developers chose to use binding function as property. I ( and maybe others ) used @event at first and of course found it didn't work.

Now I'm facing this problem. I have chosen to use binding function too but I think things can be better.

Just write your own plugin.

Ok.

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

Successfully merging this pull request may close these issues.

3 participants