npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@snowykami/use-async-task

v0.0.3

Published

A lightweight React Hook for managing complex async task states with global sharing, caching, retry, and race condition control.

Readme

@snowykami/use-async-task

npm license npm peer dependency version

一个轻量的 React Hook,用于管理复杂异步任务的状态。支持全局共享、缓存、轮询、重试与竞态控制。

核心特性

  • 🌍 跨组件共享状态 - 多个组件使用相同 taskKey 会自动共享同一份任务状态
  • 💾 智能缓存 - 可配置的缓存时间,避免重复请求相同数据
  • 🔄 自动重试 - 内置失败重试机制,支持可配置的重试次数
  • 竞态控制 - 快速请求时仅应用最后一次的请求结果,旧请求自动丢弃
  • 🔁 轮询支持 - 内置轮询间隔,支持定时自动刷新数据
  • 🎯 完整类型支持 - 100% TypeScript,提供完整的类型定义
  • 📦 超轻量 - 0 外部依赖,仅依赖 React

安装

npm install @snowykami/use-async-task
# 或者使用 yarn
yarn add @snowykami/use-async-task
# 或者使用 pnpm
pnpm add @snowykami/use-async-task

快速开始

import { useAsyncTask } from '@snowykami/use-async-task';

function MyComponent() {
  const { data, loading, error, execute } = useAsyncTask(
    async (userId: string) => {
      const res = await fetch(`/api/users/${userId}`);
      return res.json();
    },
    {
      immediate: false,
      maxRetries: 2,
      cacheTime: 10000, // 10秒缓存
      taskKey: (userId) => `user-${userId}`,
    }
  );

  return (
    <div>
      {loading && <p>加载中...</p>}
      {error && <p>错误: {error}</p>}
      {data && <p>用户: {data.name}</p>}
      <button onClick={() => execute('123')}>获取用户 123</button>
    </div>
  );
}

API 文档

useAsyncTask(action, options?, initialTaskKey?)

参数

  • action (...args: Args) => Promise<T>

    • 要执行的异步函数
    • 接收 execute() 传入的参数
  • options (可选)

    {
      // 挂载时或依赖变化时是否自动执行
      immediate?: boolean;
    
      // 依赖列表(类似 useEffect)
      dependencies?: DependencyList;
    
      // 依赖变化时获取最新参数
      getArgs?: () => Args;
    
      // 轮询间隔(毫秒),0 表示不轮询
      pollingInterval?: number;
    
      // 最大重试次数
      maxRetries?: number;
    
      // 缓存时间(毫秒),0 表示不缓存
      cacheTime?: number;
    
      // 任务标识
      // - 字符串:固定 key
      // - 函数:根据参数动态生成 key
      taskKey?: string | ((...args: Args) => string);
    }
  • initialTaskKey (可选)

    • 初始任务 key(被 options.taskKey 覆盖)

返回值

{
  // 状态属性
  data: T | null;              // 解析后的数据
  loading: boolean;            // 加载状态
  error: TError | null;        // 错误对象(如果有)
  retryCount: number;          // 重试次数
  lastUpdated: number | null;  // 最后更新时间戳

  // 方法
  execute: (...args: Args) => Promise<T | void>;  // 手动执行任务
  cancel: () => void;          // 取消当前请求
  reset: () => void;           // 重置为初始状态
}

使用示例

1. 跨组件共享状态

使用相同 taskKey 的多个组件自动共享状态:

// 组件 A
const { data: user } = useAsyncTask(
  (id: string) => fetchUser(id),
  {
    maxRetries: 3,
    taskKey: (id) => `user-${id}`,
  }
);

// 组件 B(并行运行)
const { data: user } = useAsyncTask(
  (id: string) => fetchUser(id),
  {
    maxRetries: 3,
    taskKey: (id) => `user-${id}`,  // 相同的 key!
  }
);

// 两个组件自动同步,当数据更新时一起重新渲染

2. 依赖变化时自动刷新

const [page, setPage] = useState(1);

const { data: users } = useAsyncTask(
  (p: number) => fetchUsers(p),
  {
    immediate: true,
    dependencies: [page],
    getArgs: () => [page] as const,
    taskKey: (p) => `users-page-${p}`,
  }
);

// 改变 page 时自动触发新请求

3. 搜索 + 缓存 + 竞态控制

const [query, setQuery] = useState('');

const { data: results } = useAsyncTask(
  async (q: string) => searchUsers(q),
  {
    immediate: true,
    dependencies: [query],
    getArgs: () => [query] as const,
    maxRetries: 1,
    cacheTime: 30000,  // 缓存 30 秒
    taskKey: (q) => `search-${q}`,
  }
);

// 快速输入:只处理最后一次搜索
// 重复搜索:30秒内使用缓存

4. 轮询 / 定时刷新

const { data: stats } = useAsyncTask(
  () => fetchStats(),
  {
    immediate: true,
    pollingInterval: 5000,  // 每 5 秒刷新一次
    taskKey: 'dashboard-stats',
  }
);

5. 手动表单提交

const { loading, error, execute } = useAsyncTask(
  async (formData: FormData) => submitForm(formData),
  { immediate: false }  // 不自动执行
);

const handleSubmit = async (data: FormData) => {
  const result = await execute(data);
  if (result) console.log('提交成功!');
};

全局状态共享原理

Hook 维护一个全局任务注册表,每个 taskKey 映射到一个共享状态:

taskKey: 'user-123'
  └─ TaskRecord
     ├─ state: { data, loading, error, ... }
     ├─ listeners: Set<Listener>  (所有订阅的组件)
     └─ [竞态控制信息]

当任何组件更新此状态时,所有订阅的组件自动重新渲染。

竞态控制原理

当多个请求同时进行时:

  1. 每个请求获得一个序列号
  2. 只有最新请求的结果会被应用
  3. 旧请求的结果自动被丢弃

这确保 UI 总是显示最新请求的结果。

缓存策略

在每次 execute() 调用时检查缓存:

if (Date.now() - lastUpdated <= cacheTime) {
  // 使用缓存数据,跳过请求
  return cachedData;
} else {
  // 缓存过期,发起新请求
  // 用新数据更新缓存
}

不同的 taskKey 拥有独立的缓存。

最佳实践

  1. 使用具有描述性的 taskKey - 便于调试

    taskKey: (userId) => `user-profile-${userId}`  // ✓ 好
    taskKey: (x) => `${x}`  // ✗ 模糊
  2. 使用 dependencies 时总是提供 getArgs

    {
      dependencies: [page],
      getArgs: () => [page] as const,  // ✓ 提供新值
    }
  3. 根据数据新鲜度设置合适的缓存时间

    cacheTime: 0,         // 不缓存(实时数据)
    cacheTime: 5000,      // 5 秒(不那么关键的数据)
    cacheTime: 300000,    // 5 分钟(参考数据)
  4. 对不稳定的端点使用重试

    maxRetries: 0,   // 不重试(关键且需要快速失败)
    maxRetries: 3,   // 重试最多 3 次(标准)

浏览器支持

  • ES2020+
  • React 16.8+(需要 Hooks 支持)

许可证

MIT © snowykami

贡献

欢迎提交 Issue 和 PR!

更新日志

v1.0.0 (首次发布)

  • 核心 Hook 实现
  • 全局状态共享
  • 缓存系统
  • 重试逻辑
  • 轮询支持
  • 竞态控制