Skip to content
文章目录

将异步方法的逻辑封装进hook中

ts
import { ref } from 'vue'

type AsyncMethod<R, P extends unknown[]> = (...args: P) => Promise<R>
interface Options<R, P> {
  /**
   * 是否手动请求. 默认:false
   *
   * false表示asyncMethod方法会在组件created时自动调用, true表示asyncMethod方法不会在组件created时自动调用,必须手动调用run方法才会真正执行
   */
  manual: boolean
  /**
   * 当manual不为false时,会使用initData作为参数自动执行run方法
   */
  initData?: P
  /**
   * asyncMethod 执行成功之后的回调方法
   *
   * @param   {R}  ret  asyncMethod的返回值
   */
  onSuccess?: (ret: R) => void
  /**
   * asyncMethod 执行异常时的回调方法
   *
   * @param   {unknown}  err  异常对象
   *
   */
  onError?: (err: unknown) => void
  /**
   * 无论asyncMethod方法执行成功或失败都会调用的回调方法
   */
  onFinally?: () => void
}

export function useAsync<R = unknown, P extends unknown[] = any>(
  asyncMethod: AsyncMethod<R, P>,
  options = {} as Options<R, P>
) {
  const { manual = false, initData, onSuccess, onError, onFinally } = options
  const loading = ref<boolean>(false)
  const data = ref<R>()
  const error = ref<unknown>()

  async function run(...args: P): Promise<R | undefined> {
    loading.value = true
    try {
      const ret = await asyncMethod(...args)
      data.value = ret
      onSuccess && onSuccess(ret)
      return ret
    } catch (err) {
      onError && onError(err)
      error.value = err
    } finally {
      loading.value = false
      onFinally && onFinally()
    }
  }
  if (!manual) {
    if (initData) {
      run(...initData)
    } else {
      // @ts-ignore
      run()
    }
  }
  return { loading, data, error, run }
}