0.2.10 • Published 9 months ago

@magic-js/use-async v0.2.10

Weekly downloads
-
License
MIT
Repository
github
Last release
9 months ago

@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> {
  // 扩展自定义事件钩子
}
0.2.10

9 months ago

0.2.9

9 months ago

0.2.8

10 months ago

0.2.1

10 months ago

0.2.0

10 months ago

0.2.7

10 months ago

0.2.6

10 months ago

0.2.3

10 months ago

0.2.2

10 months ago

0.2.5

10 months ago

0.2.4

10 months ago

0.1.0

10 months ago