Skip to content

l246804/use-async

Repository files navigation

@magic-js/use-async

一个高性能可扩展的 Vue3 异步任务流管理工具,推荐搭配 async-plugin-request 处理网络请求。

useAsync 的执行阶段事件钩子支持异步回调,每个钩子回调会等待上一个回调结束再执行。

安装

# npm
npm i @magic-js/use-async

# yarn
yarn add @magic-js/use-async

# pnpm
pnpm add @magic-js/use-async

使用方式

基础示例

import { useAsync } from '@magic-js/use-async'

function fetchData(num: number) {
  if (num > 10) {
    return Promise.reject(new Error('error: num > 10'))
  }

  return Promise.resolve(num)
}

;(async () => {
  // 自动执行
  const { data, error, execute, reExecute, on, then } = await useAsync(fetchData, { initialPayload: [1] })
  data.value // => 1

  // 再次执行
  await execute(10).then((value) => {
    console.log(value) // => 10
  })

  // 基于最后一次参数重新执行
  await reExecute().then((value) => {
    console.log(value) // => 10
  })

  // 执行后报错可以通过 error.value 获取最近一次错误信息
  await execute(100)
  error.value // => Error { message: 'error: num > 10' }

  // 动态注册执行流回调
  on('before', (ctx) => {
    console.log(ctx.payload) // => [200]
  })
  await execute(200)

  // 多次执行时返回结果均为对应的数据,data.value 和 error.value 仅为最近一次执行完成时的状态记录
  execute(5).then((value) => {
    console.log(value) // => 5
  })
  execute(6).then((value) => {
    console.log(value) // => 6
  })
  data.value // => 6,如果 `execute(5)` 执行结果晚于 `execute(6)` 时,这里为 `5`
})()

// 手动执行,默认不再自动执行
const { execute } = useAsync(fetchData, { immediate: false })

使用插件

安装插件库

pnpm add async-plugin-request

全局使用

// hooks/useAsync.ts
import { createAsync } from '@magic-js/use-async'
import { createAxiosPlugin } from 'async-plugin-request/axios'

export const useAsync = createAsync({
  // 注册 AxiosPlugin,用于链接 `useAsync()` 和 `axios()`
  plugins: [createAxiosPlugin()]
})
import axios from 'axios'
import { useAsync } from 'hooks/useAsync'

function fetchData(params: any) {
  return axios.get('/api/getList', { params })
}

const { abort } = useAsync(fetchData, {
  initialPayload: [{ a: 1 }],
  axiosConfig: {
    timeout: 10e3 // 这里的配置项会透传给 axios
  },
})

abort() // 同步终止 axios 请求

局部使用

import { useAsync } from '@magic-js/use-async'
import { createAxiosPlugin } from 'async-plugin-request/axios'
import axios from 'axios'

function fetchData(params: any) {
  return axios.get('/api/getList', { params })
}

const { abort } = useAsync(fetchData, {
  initialPayload: [{ a: 1 }],
  // 注册 AxiosPlugin,用于链接 `useAsync()` 和 `axios()`
  plugins: [createAxiosPlugin()],
  axiosConfig: {
    timeout: 10e3 // 这里的配置项会透传给 axios
  },
})

abort() // 同步终止 axios 请求

开发插件

useAsync 负责管理异步任务的执行流,会在不同的阶段抛出相应的事件,可通过插件对异步任务管理进行额外的功能扩展。

插件的事件回调会早于 createAsyncuseAsync 执行。

示例

该插件已被包含在 async-plugin-request 中!

import { createError } from '@magic-js/use-async'

/**
 * 创建动态刷新令牌插件
 */
export function createRefreshTokenPlugin(pluginOptions) {
  const { enabled: baseEnabled = true, assertExpired, handler } = pluginOptions

  let refreshPromise = null
  return function RefreshTokenPlugin(pluginCtx) {
    const { task: rawTask, shell } = pluginCtx

    // 在 before 阶段包装原始任务
    shell.on('before', async (ctx) => {
      const { enabled = baseEnabled } = ctx.options.refreshToken || {}
      const refreshTokenCtx = {
        ...pluginCtx,
        abort: () => ctx.abort(),
        isAborted: ctx.isAborted
      }

      // 包装原始任务
      pluginCtx.task = async (ctx) => {
        // 没有启用时直接返回原始任务的执行
        if (!enabled)
          return rawTask(ctx)

        // 存在刷新操作时等待完成后再执行原始任务
        if (refreshPromise) {
          return refreshPromise.then(() => rawTask(ctx))
        }

        try {
          // 正常执行原始任务
          return await rawTask(ctx)
        }
        catch (e) {
          const error = createError(e)

          // 断言是否为令牌过期导致失败
          if (await assertExpired(error)) {
            // 设置刷新操作
            if (!refreshPromise) {
              refreshPromise = new Promise((resolve) => {
                resolve(handler({ ...refreshTokenCtx, abort: () => ctx.abort(error) }))
              })
            }

            // 等待刷新成功后再次执行原始任务
            return refreshPromise
              .then(() => !refreshTokenCtx.isAborted() && rawTask(ctx))
              // 刷新失败后抛出原始错误信息
              .catch(() => Promise.reject(error))
              .finally(() => {
                refreshPromise = null
              })
          }

          // 非令牌过期导致出错时直接抛出错误
          throw error
        }
      }
    })
  }
}

类型扩展

tsconfig.json 所包含的文件目录中创建任意 .ts.d.ts 文件添加如下代码。

interface CreateAsyncOptions {
  // 扩展自定义 `createAsync` 配置项
}

interface UseAsyncOptions<T> {
  // 扩展自定义 `useAsync` 配置项
}

interface UseAsyncReturn<T> {
  // 扩展自定义 `useAsync` 返回
}

interface UseAsyncHooks<T> {
  // 扩展自定义事件钩子
}

Polyfill

在不支持现代 AbortSignal 的环境下可以通过 abortcontroller-polyfill 进行填充。

安装

# npm
npm i abortcontroller-polyfill

# yarn
yarn add abortcontroller-polyfill

# pnpm
pnpm add abortcontroller-polyfill

使用方式

更多使用方式推荐查阅官方说明

// main.ts
// 仅对运行环境原生的 AbortController 和 AbortSignal 支持不完善时推荐使用该方式
import 'abortcontroller-polyfill/dist/abortsignal-polyfill-only'

// 对运行环境填充自定义 AbortController 和 AbortSignal
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'

About

异步任务管理器。

Resources

License

Stars

Watchers

Forks

Packages

No packages published