vueuse icon indicating copy to clipboard operation
vueuse copied to clipboard

useAsyncFunction

Open visualjerk opened this issue 3 years ago • 0 comments

Clear and concise description of the problem

I would love a hook, that makes getting the pending state of an async function easier.

Currently I find myself writing this kind of boilerplate a lot:

import { logout } from '@/store/auth'

const logoutPending = ref(false)

async function handleLogout() {
  logoutPending.value = true
  await logout()
  logoutPending.value = false
}

Therefore I created a use hook for that and would like to submit a PR to implement this hook for VueUse. It can be used as follows:

import { logout } from '@/store/auth'
import { useAsyncFunction } from '@/utils'

const { pending: logoutPending, trigger: handleLogout } = useAsyncFunction(logout)

Suggested solution

Simple Implementation

In the core module, we could provide this implementation:

import { Ref } from 'vue'

export function useAsyncFunction<TArgs extends any[], TReturn extends any>(
  fn: (...args: TArgs) => Promise<TReturn>
): {
  pending: Ref<boolean>
  trigger: (...args: TArgs) => Promise<TReturn | undefined>
} {
  const pending = ref(false)

  async function trigger(...args: TArgs) {
    if (pending.value) {
      return
    }
    pending.value = true
    const result = await fn(...args)
    pending.value = false
    return result
  }

  return {
    pending,
    trigger,
  }
}

Possible features to enhance this hook

  • state or data ref that contains the latest result
  • abort method to cancel the running async call(s)
  • allow trigger to be called multiple times and handle pending for multiple calls
  • add abortPendingOnTrigger option to automatically abort pending calls when triggering a new one
  • add pendingCount option to see how many calls are currently pending
  • add accumulateResults option to make state be an array of results that were returned

Alternative

Just figured out, that useAsyncState can achieve something similar:

const { execute, isLoading } = useAsyncState(() => logout(), null, {
  immediate: false,
})

It might be enough to add some examples to the docs of useAsyncState that show these use cases. However we could still provide a thin wrapper around useAsyncState that makes the usage more intuitive for the use case explained above.

Additional context

No response

Validations

visualjerk avatar Jul 25 '22 18:07 visualjerk