有了useAsyncState可以不用手动声明loading表示请求状态了

2,539 阅读3分钟

前端在发起网络请求的时候往往要做这样几件事情:声明一个loading变量表示请求状态,发起请求时设置为true,结束请求时设置为false;处理网络请求的异常情况;某些场景还需要重置请求结果。对于开发同学当然是越省事儿越好,useAsyncState刚好可以帮我们解决问题。今天就来学习useAsyncState的使用和源码,学习完您可以用到自己的vue3项目中。

简介

useAsyncState提供了响应式的异步状态。

示例

看一下官方文档给出的示例代码:

<script setup lang="ts">
  import axios from 'axios'
  import YAML from 'js-yaml'
  import { useAsyncState } from '@vueuse/core'
  
  const { isLoading, state, isReady, execute } = useAsyncState(
    (args) => {
      const id = args?.id || 1
      return axios.get(`https://jsonplaceholder.typicode.com/todos/${id}`).then(t => t.data)
    },
    {},
    {
      delay: 2000,
      resetOnExecute: false,
    },
  )
</script>

<template>
  <div>
    <note>Ready: {{ isReady.toString() }}</note>
    <note>Loading: {{ isLoading.toString() }}</note>
    <pre lang="json" class="ml-2">{{ YAML.dump(state) }}</pre>
    <button @click="execute(2000, { id: 2 })">
      Execute
    </button>
  </div>
</template>

如上代码引入了useAsyncState,并执行useAsyncState从返回结果中解构出isLoading, state, isReady, execute。调用useAsyncState的参数分别为能够返回promise的函数,初始state为一个空对象,可选的options对象的两个属性delay为延迟执行时间,resetOnExecute表示执行网络请求之前是否重置state。

代码执行效果如下图所示,您可以访问官网或者写demo自己试一下:

点击Execute后的效果如下图:

源码

我们预览一下useAsyncState方法体:

参数说明

function useAsyncState<Data, Shallow extends boolean = true>(
  promise: Promise<Data> | ((...args: any[]) => Promise<Data>),
  initialState: Data,
  options?: AsyncStateOptions<Shallow>,
)

promise 为一个Promise或者返回Promise的方法;initialState用于重置state;options为可选的,有如下几个选项:delay为执行promise的延迟时间,onError为执行promise时捕获到错误时的回调,resetOnExecute如果为true则在执行promise之前重置state;shallow为true则使用vue的shallowRef创建state,否则使用Ref。AsyncStateOptions的定义如下:

export interface AsyncStateOptions<Shallow extends boolean> {
  delay?: number
  immediate?: boolean
  onError?: (e: unknown) => void
  resetOnExecute?: boolean
  shallow?: Shallow
}

state初始化

const state = shallow ? shallowRef(initialState) : ref(initialState)
const isReady = ref(false)
const isLoading = ref(false)
const error = ref<unknown | undefined>(undefined)

根据initialState创建出的Ref赋值给state;isReady初始化为false, isLoading也初始化为false,error初始化为undefined。

execute方法定义

async function execute(delay = 0, ...args: any[]) {
    if (resetOnExecute)
      state.value = initialState
    error.value = undefined
    isReady.value = false
    isLoading.value = true

    if (delay > 0)
      await promiseTimeout(delay)

    const _promise = typeof promise === 'function'
      ? promise(...args)
      : promise

    try {
      const data = await _promise
      state.value = data
      isReady.value = true
    }
    catch (e) {
      error.value = e
      onError(e)
    }
    finally {
      isLoading.value = false
    }

    return state.value as Data
  }

execute方法的第一个参数为delay表示promise延迟执行的时间,其余的参数用args接收。如果resetOnExecute为真值,则初始化state。接着给error、isReady和isLoading赋予对应的值。如果delay大于0则执行一段延时。判断上级作用域的promise参数是函数还是promise对象,如果是函数则先执行函数,函数的执行结果为一个promise。接着在try语句块中执行promise,根据promise的执行结果修改state的值,然后修改isReady的值为true。如果发生了异常则给error赋值并触发onError回调。在finally语句块中修改isLoading的值为false。最后返回state。下图描述了execute的执行过程:

立即执行execute

if (immediate)
    execute(delay)

如果options的immediate选项为true则立即执行execute方法。

返回状态

return {
  state: state as Shallow extends true ? Ref<Data> : Ref<UnwrapRef<Data>>,
  isReady,
  isLoading,
  error,
  execute,
}

注意在返回对应的响应式值的时候也返回了execute方法。

总结

useAsyncState对异步请求进行了封装。对于loading状态、异常(error)等都定义为响应式变量和包装后的请求方法一起暴露给开发者。整个useAsyncState的核心又在于execute方法的定义。useAsyncState主要用到的vue3 API是shallowRef和ref。