Comments (19)
One drawback though, is that you can't direct access evaluating
in the template then. Might be better to pass it as a ref (or evaluating
& evaluatingRef
as the same time, not sure)
from vueuse.
I want it several times too! Looks promising and let's have it!
Some thoughts,
- for the naming, I think
asyncComputed
is simpler & neater. - for the returning value, I am thinking if we could get rid of the tuple, I would like it to be more like
computed
(sometimes ppl don't care if it's evaluating)
How about:
const state = reactive({ value: defaultValue, evaluating: false }) as { value: T, evaluating: Boolean }
/* ... */
return state
from vueuse.
I agree on the naming thing, I just decided on use*
to stick with the rules. 😁
However, I disagree on the usage of reactive
, for various reasons:
-
It wouldn't actually bring it close enough to
computed
. While access in code would be equivalent in code usage (computedThing.value
vsasyncComputedThing.value
), it would differ in template usage (computedThing
vsasyncComputedThing.value
).More in general, I'd avoid naming a property of a reactive object
value
. This would probably be a real stumbling block for people not familiar with the code base, and they might assumeasyncComputedThing
is a ref because it has a.value
.So, if we'd go the
reactive()
road, we should probably name it something likestate
orresult
. -
However, I'd assume that people mostly really don't care for the
evaluating
part. Returning a tuple makes it easy to just ignore that part by omitting it from destructuring. Having an object with two properties, on the other hand, would mean additional work for all those people who don't care aboutevaluating
because they had to use the extra accessor to get the computed value.
Alternative solutions:
-
Omit the
evaluating
part.This was how I implemented the function in the first place. However, I realized that there are probably enough use cases for it (e.g. showing a loading state instead of stale data) that having this would be desirable.
-
Provide the data in another way.
We would not have to return the
evaluating
data. An alternative approach would be to provide another callback.So instead of using the function like this...
const [downloads, isEvaluating] = asyncComputed(onCancel => { onCancel(() => { /* abort fetch */ }) return fetch(/* and so on */) }, 0)
...we could provide something like a
onStartStop
(naming is debatable, don't like it quite yet) callback which is triggered when the computed starts/finishes evaluating:const isEvaluating = ref(false) const downloads = asyncComputed(({ onCancel, onStartStop }) => { onCancel(() => { /* abort fetch */ }) onStartStop(state => { isEvaluating.value = state }) return fetch(/* and so on */) }, 0)
Advantage: It would keep the return value clean, which would supposedly remove noise and mental burden for most users.
Disadvantage: It would slightly complicate the previously very simple callback argument (we'd have to provide a destructurable object à la{ onCancel, onStartStop }
instead of just pass theonCancel
function) for a smaller fraction of users.
from vueuse.
Actually, you can pretend any object to be a ref like
const data = ref(defaultValue)
const evaluating = ref(false)
const state = {
__v_isRef: true, // this will make Vue think it's a ref
get value() {
return data.value
},
get evaluating() {
return evaluating.value
}
} as Readonly<Ref<T> & { evaluating: Boolean }>
return state
This is also how toRef
implemented.
So basically, asyncComputed
will return a Ref but with an additional property evaluating
. Just like the computed ref actually has an additional property effect
if you don't know.
What do you think?
from vueuse.
This is fascinating.
Still one complaint though: evaluating
now is no longer reactive. We could of course return the ref itself instead of its value (so it could be accessed as state.evaluating.value
), but this modification of the ref type seems to be a little off the beaten track for Vue stuff.
Is there any known prior art using this technique? (The question is probably moot, given the relatively young age of the Composition API, but anyway.)
So, while I find this solution pretty cool from a DX point of view, I'd be careful to mess with known patterns. But I don't dislike it at all. Do you have any further place to gather feedback from regarding the shape of the API?
from vueuse.
evaluating now is no longer reactive
No, I believe it is. You can have a try.
Is there any known prior art using this technique?
AFAIK, no. I know some of my friends are using them in their private apps. And I am experimenting with a lot of things in @vue-reactivity (private too). I always want to use them in VueUse in order to get some clean interface but I haven't got time to do so. Let's be the first one!
I'd be careful to mess with known patterns
It's no harm as I can tell for now. I will be your eyes :P
from vueuse.
evaluating now is no longer reactive
No, I believe it is. You can have a try.
Sorry, I have not been clear enough with what I meant.
Of course, the state.evaluating
getter would always return the correct value. But it would not be reactive in Vue's eyes (i.e. you could not use state.evaluating
in a template and could not watch it).
This would be fixable though by implementing it like this:
const data = ref(defaultValue)
const evaluating = ref(false)
const state = {
__v_isRef: true, // this will make Vue think it's a ref
get value() {
return data.value
},
evaluating
} as Readonly<Ref<T> & { evaluating: Boolean }>
return state
So we'd not return the current value for state.evaluating
, but the ref itself, so it could be used in a template and accessed via state.evaluating.value
.
from vueuse.
See comment above. 😁
from vueuse.
evaluating
as Getter
const data = asyncComputed(async () => { /*...*/ })
return {
data,
evaluating: computed(() => data.evaluating)
}
evaluating
as Ref
const data = asyncComputed(async () => { /*...*/ })
return {
data,
evaluating: data.evaluating
}
As an object of refs
const { data, evaluating } = asyncComputed(async () => { /*...*/ })
return {
data,
evaluating,
}
Or
return {
...asyncComputed(async () => { /*...*/ })
}
As an array of refs
const [data, evaluating] = asyncComputed(async () => { /*...*/ })
return {
data,
evaluating,
}
Yeah, I agree that the hack is not ideal to be used in templates. Maybe it's head back to the traditional implementation.
from vueuse.
Any concrete objections against the onStartStop
approach from above? (As I said, naming is terrible, it's just as a proof of concept.)
from vueuse.
I don't think we actually need onStartStop
as I can just put it at the end of my async function. About the onCancel
part, I don't think we should provide a function call here. Instead, an object called cancelToken might be better:
something like:
asyncComputed((cancelToken)=>{
const a = await fetch(xxx)
if (cancelTolen.cancelled)
return // early return
cosnt b = await fetch(a)
return [a, b]
})
from vueuse.
Inspiration from VS Code Extensions https://vscode.readthedocs.io/en/stable/extensions/patterns-and-principles/#cancellation-tokens
from vueuse.
I don't think we actually need
onStartStop
as I can just put it at the end of my async function.
Sorry, I don't get it. Could you elaborate on that? What could you put at the end of your async function?
Instead, an object called cancelToken might be better
I don't really care about the implementation details. (My inspiration for onCancel
came from the Composition API's very own watchEffect
.)
Regarding the cancel token: How would you implement a notification system on the token? EventEmitter
is a Node-only API, EventTarget
is web-only. Implementing a custom event emitter just for our little function would be overkill, so the remaining options would probably be a Vue instance with a very reduced emitter (only allowing a single listener assigned to e.g. a $on/$emit
orcancelToken.onCancel
property)?
from vueuse.
onStartStop
Let's keep the evaluating
state inside the function, using events and let users doing that might be too much.
onCancel
I see. I agree it's better to stay with the Vue style. How about just keep it named as onInvalidate
?
Sorry for being a bit nitpicking, this is a good direction and I'd say let's go for it. Thanks a lot 👍
from vueuse.
No worries, that's great. Let's be nitpicky now — we probably won't be able to change the design later. 🙂
Let's keep the
evaluating
state inside the function
Sorry, still don't get it — where would you like to keep it?
How about just keep it named as
onInvalidate
?
I deviated from the original name on purpose because the behavior is slightly different: watchEffect
has no mechanisms to detect whether previous callbacks finished their work, therefore onInvalidate
is always called when the next effect is invoked — whereas onCancel
is only called when the next evaluation is started and the previous evaluation has not finished yet (which we can track thanks to the returned promise).
Of course we could resort to calling it onInvalidate
, but then we'd also have to adopt the described watchEffect
behavior to avoid confusion. However,
-
we would somehow still need to tell the user whether the previous evaluation finished, likely with a parameter to the callback
onInvalidate
invokes:asyncComputed(onInvalidate => { onInvalidate(hasFinished => { // `hasFinished` would be a boolean indicating whether the // previous evaluation has finished, naming is up for debate. }) return somePromise })
However, this would then again deviate from the
watchEffect
behavior as theironInvalidate
does not provide such a boolean. -
I think it's reasonable to not provide users with more information than they need.
They should be notified when evaluation is canceled because they may have to do cleanup work, but I don't see any value in them being notified about the next evaluation if their own task has already finished.
Therefore I settled for onCancel
.
from vueuse.
Let's keep the evaluating state inside the function
I think it's good to keep your original version :p
const [downloads, isEvaluating] = asyncComputed(onCancel => { })
Therefore I settled for onCancel
I got it, thanks a lot for the detailed explanation. Let's go for it!
from vueuse.
My pleasure. Want me to create a PR?
from vueuse.
It will be great if you don't mind. Thanks 😁
from vueuse.
I'll give it a try. First contact with Storybook for me, but the other functions should provide enough learning material. 🙂
from vueuse.
Related Issues (20)
- toReactive nullish initial value HOT 2
- 🚀 New composable `resettableRef`
- 🐛 Awaiting useFetch in computedAsync causes infinite requests HOT 2
- Why don't support 'maxAlternatives' in UseSpeechRecognitionOptions? HOT 1
- `onLongPress` : callback hook is fired only once when long press HOT 6
- Enhance useVibrate to return the value from navigator.vibrate method
- watchEffect with a callback(without tracking)
- useDropZone files have 0 size HOT 1
- useBreakpoints adds elementPlus preset
- useScroll - Opt out of updating internal values
- useDebounceFn should support accepting functions that return promises as well as void
- useFocusWithin doesn't respond to document.hasFocus()
- Make API's of `useDropZone` and `useFileDialog` consistent HOT 1
- window.addEventListener('scroll', handleScroll); not working when add the UseScreenSafeArea HOT 1
- useMagicKeys needs the keys to be pressed in order (sometimes)
- `{combination: 'overwrite'}` option for `useFetch`
- Dependency error when `vue` is not part of the dependencies in a project
- Function wrapper for `requestAnimationFrame` synchronization
- onLongPress optional onMouseUp callback should be called with the PointerEvent
- useBreakpoint and useMediaQuery cause "Hydration text mismatch" on Nuxt HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from vueuse.