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

asyncComputed is not lazy like computed #248

Closed
Demivan opened this issue Dec 13, 2020 · 7 comments · Fixed by #247
Closed

asyncComputed is not lazy like computed #248

Demivan opened this issue Dec 13, 2020 · 7 comments · Fixed by #247

Comments

@Demivan
Copy link
Contributor

Demivan commented Dec 13, 2020

Not sure if this should be a default but, I think, there should be option to make asyncComputed lazy like vue computed. Right now even if you use v-if to not render computed data it is still being retrieved.

#247 - Example of failing test

@patak-dev
Copy link
Member

I do not know if this can be fixed if the current API is maintained. I managed to get an asyncComputed version that is lazy like computed but it doesn't provide the onCancel callback:

export function asyncComputed<T>(
  evaluationCallback: () => T | Promise<T>,
  initialState?: T,
  evaluatingRef?: Ref<boolean>,
): Ref<T> {
  return customRef<T>((track: Fn, trigger: Fn) => {
    let counter = 0
    let current = initialState

    const tracker = computed(() => {
      counter++
      const counterAtBeginning = counter

      // Defer initial setting of `evaluating` ref
      // to avoid having it as a dependency
      if (evaluatingRef) {
        Promise.resolve().then(() => {
          evaluatingRef.value = true
        })
      }

      Promise.resolve(evaluationCallback()).then((result) => {
        if (counterAtBeginning === counter)
          current = result
      }).finally(() => {
        if (evaluatingRef)
          evaluatingRef.value = false
        trigger()
      })

      return counter
    })

    return {
      get() {
        track()
        // eslint-disable-next-line no-unused-expressions
        tracker.value
        return current as T
      },
      set() {},
    }
  })
}

There was an issue about this in vuejs/rfcs#163. If we would have a invalidableComputed helper that provides onInvalidate the above implementation could be extended to support the current API.
I tried using computed + watch to create this helper, but the watch ends up triggering the computed. watchEffect is not lazy so it will also trigger it. Maybe there is a way to do this but I couldn't find it... invalidableComputed could be a great addition to vueuse if there is a way to do it.

Just an idea, but one possible option is to offer the above version as asyncComputed without the onCancel callback, and keep the current implementation with another name. Maybe something like asyncRef?

@antfu
Copy link
Member

antfu commented Dec 14, 2020

The behaviour has already described as a caveat in the doc. I am not sure if it makes sense to have asyncComputed lazy as in that way you will always get the null on the first access (and need to wait for the promise get resolved from that time).

Re: invalidableComputed

With vuejs/rfcs#212 (likely in Vue 3.1), it will expose effect to vue and we can make invalidableComputed easily with it. However, that would be a Vue 3 only thing (Vue 2 does not have effect).

@patak-dev
Copy link
Member

Nice! I didn't know that effect was going to be exposed 🙌🏼
Maybe at one point, there could be a lazy option. I agree that with asyncComputed the current behavior should be the default.

@Demivan
Copy link
Contributor Author

Demivan commented Dec 14, 2020

Sorry, somehow missed caveat section in the docs.
I fixed my use case by extracting part of template that was hidden using v-if to separate component and moving asyncComputed there.
Should I close this issue or lazy option is being considered? It would be useful in some cases.

@antfu
Copy link
Member

antfu commented Dec 14, 2020

@Demivan Sure, let's have the lazy option. Do you mind to create a PR?

@Demivan
Copy link
Contributor Author

Demivan commented Dec 14, 2020

Sure, I'll work on it.

@bodograumann
Copy link
Contributor

The current implementation of the lazy option still has the drawback that "As opposed to Vue's built-in computed function, re-evaluation of the async computed value is triggered whenever dependencies are changing, regardless of whether its result is currently being tracked or not.", once the evaluation has been started by the first .value access.

Do you think it is feasible to solve this issue with the current vue features, @antfu, maybe using @patak-dev 's idea above? It seems effect is indeed exposed now, but I could not find it on the documentation page.
I would open a new issue, if it makes sense to tackle this task at all.

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 a pull request may close this issue.

4 participants