# AsyncTaskManager 异步任务管理器

专为 Taro 小程序设计的通用异步任务管理工具，支持任务轮询、状态监控、后台运行恢复等功能。

## ✨ 功能特点

- 🔄 **智能轮询**: 自动轮询任务状态，支持自定义间隔
- 🏃‍♂️ **后台运行**: 应用切换后台后自动暂停，恢复前台时继续轮询  
- 💾 **状态持久化**: 任务状态本地存储，应用重启后自动恢复
- ⏰ **超时控制**: 自动检测任务超时，可自定义超时时间
- 🔁 **智能重试**: 网络异常时自动重试，可配置重试次数
- 📞 **事件回调**: 支持进度、成功、失败、超时等回调函数
- 🚫 **任务取消**: 支持主动取消正在运行的任务
- 🧪 **测试友好**: 完整的单元测试覆盖，支持依赖注入

## 📦 安装使用

### 1. 基础用法

```typescript
import { AsyncTaskManager, TaskStatus, TaskConfig } from '@/utils/AsyncTaskManager';

// 创建任务管理器实例
const taskManager = new AsyncTaskManager();

// 配置任务
const config: TaskConfig = {
  taskId: 'your-task-id',       // 任务ID
  pollingInterval: 3000,        // 3秒轮询间隔
  maxRetries: 3,               // 最大重试3次
  timeout: 300000,             // 5分钟超时
  onProgress: (status) => {
    console.log('任务状态:', status);
  },
  onSuccess: (result) => {
    console.log('任务完成:', result);
  },
  onError: (error) => {
    console.error('任务失败:', error);
  },
  onTimeout: () => {
    console.log('任务超时');
  }
};

// 启动任务
await taskManager.startTask(config);
```

### 2. 使用 Hook（推荐）

```typescript
import { useTaskOperations } from '@/hooks/useAsyncTaskManager';

export default function MyPage() {
  const { submitImageTask, cancelTask, getTaskStatus } = useTaskOperations();

  const handleSubmit = async () => {
    try {
      const taskId = await submitImageTask(
        'https://example.com/image.jpg',
        'sketch-style',
        (status) => console.log('进度:', status),
        (result) => console.log('完成:', result),
        (error) => console.error('失败:', error)
      );
      
      console.log('任务已提交:', taskId);
    } catch (error) {
      console.error('提交失败:', error);
    }
  };

  return (
    <Button onClick={handleSubmit}>
      开始处理
    </Button>
  );
}
```

## 🔧 API 参考

### TaskStatus 枚举

```typescript
enum TaskStatus {
  PENDING = 'pending',      // 待开始
  RUNNING = 'running',      // 执行中  
  SUCCESS = 'success',      // 成功
  FAILED = 'failed',        // 失败
  TIMEOUT = 'timeout',      // 超时
  CANCELLED = 'cancelled'   // 已取消
}
```

### TaskConfig 接口

```typescript
interface TaskConfig {
  taskId: string;                    // 任务ID（必需）
  pollingInterval: number;           // 轮询间隔，单位毫秒（必需）
  maxRetries: number;               // 最大重试次数（必需）
  timeout: number;                  // 超时时间，单位毫秒（必需）
  onProgress?: (status: TaskStatus, data?: any) => void;  // 进度回调
  onSuccess?: (result: any) => void;                     // 成功回调
  onError?: (error: Error) => void;                      // 错误回调
  onTimeout?: () => void;                                // 超时回调
}
```

### AsyncTaskManager 方法

```typescript
class AsyncTaskManager {
  // 启动新任务
  async startTask(config: TaskConfig): Promise<void>
  
  // 取消任务
  async cancelTask(taskId: string): Promise<void>
  
  // 检查任务是否存在
  hasTask(taskId: string): boolean
  
  // 获取任务状态
  getTaskStatus(taskId: string): TaskStatus | null
  
  // 暂停所有轮询（应用切后台时调用）
  pausePolling(): void
  
  // 激活所有轮询（应用恢复前台时调用）
  activatePolling(): void
  
  // 恢复任务（应用启动时调用）
  async resumeTasks(): Promise<void>
  
  // 设置状态检查函数（用于自定义API调用）
  setStatusChecker(checker: (taskId: string) => Promise<TaskStatus>): void
}
```

## 🎯 使用场景

### 1. 图像处理任务

```typescript
const handleImageProcess = async (imageUrl: string) => {
  const config: TaskConfig = {
    taskId: await submitImageToAPI(imageUrl),
    pollingInterval: 2000,    // 2秒轮询
    maxRetries: 3,
    timeout: 180000,          // 3分钟超时
    onProgress: (status) => {
      switch (status) {
        case TaskStatus.RUNNING:
          Taro.showLoading({ title: '处理中...', mask: true });
          break;
        case TaskStatus.SUCCESS:
          Taro.hideLoading();
          Taro.showToast({ title: '处理完成', icon: 'success' });
          break;
      }
    },
    onSuccess: (result) => {
      // 显示处理结果
      setResultImage(result.outputUrl);
    },
    onError: (error) => {
      Taro.showToast({ title: '处理失败', icon: 'error' });
    }
  };

  await taskManager.startTask(config);
};
```

### 2. 文件上传任务

```typescript
const handleFileUpload = async (filePath: string) => {
  const config: TaskConfig = {
    taskId: await startUploadTask(filePath),
    pollingInterval: 1000,    // 1秒轮询
    maxRetries: 5,           // 上传可能网络不稳定，多重试几次
    timeout: 600000,         // 10分钟超时
    onProgress: (status, data) => {
      if (data?.progress) {
        setUploadProgress(data.progress);
      }
    },
    onSuccess: (result) => {
      console.log('上传完成:', result.fileUrl);
    }
  };

  await taskManager.startTask(config);
};
```

### 3. 数据分析任务

```typescript
const handleDataAnalysis = async (datasetId: string) => {
  const config: TaskConfig = {
    taskId: await submitAnalysisJob(datasetId),
    pollingInterval: 5000,    // 5秒轮询（分析任务通常较慢）
    maxRetries: 2,
    timeout: 1800000,        // 30分钟超时
    onProgress: (status) => {
      updateAnalysisStatus(status);
    },
    onSuccess: (result) => {
      displayAnalysisReport(result);
    }
  };

  await taskManager.startTask(config);
};
```

## 🔄 生命周期集成

### 在 App.tsx 中集成

```typescript
import { useAsyncTaskManager } from '@/hooks/useAsyncTaskManager';

function App({ children }) {
  const taskManager = useAsyncTaskManager();

  useLaunch(() => {
    // 应用启动时恢复任务
    taskManager?.resumeTasks();
  });

  useDidShow(() => {
    // 应用显示时激活轮询
    taskManager?.activatePolling();
  });

  useDidHide(() => {
    // 应用隐藏时暂停轮询
    taskManager?.pausePolling();
  });

  return <Provider store={store}>{children}</Provider>;
}
```

## 🧪 测试

运行测试：

```bash
npm test AsyncTaskManager
```

测试覆盖了以下场景：
- ✅ 任务创建和状态管理
- ✅ 轮询机制和定时器控制
- ✅ 回调函数调用
- ✅ 超时处理
- ✅ 持久化存储
- ✅ 错误处理和重试
- ✅ 任务暂停和恢复

## ⚠️ 注意事项

### 1. 小程序环境限制

- 小程序切后台5分钟后会被暂停，长时间任务需要提醒用户保持前台
- 网络请求有并发限制，避免创建过多同时运行的任务

### 2. 性能优化

```typescript
// ❌ 避免过短的轮询间隔
const badConfig = {
  pollingInterval: 100  // 100ms 太频繁
};

// ✅ 合理的轮询间隔
const goodConfig = {
  pollingInterval: 3000  // 3秒比较合适
};
```

### 3. 错误处理

```typescript
const config: TaskConfig = {
  // ... 其他配置
  onError: (error) => {
    // 根据错误类型进行不同处理
    if (error.message.includes('网络')) {
      Taro.showToast({ title: '网络连接异常', icon: 'none' });
    } else if (error.message.includes('授权')) {
      // 跳转到授权页面
    } else {
      Taro.showToast({ title: '处理失败，请重试', icon: 'error' });
    }
  }
};
```

### 4. 状态检查函数

```typescript
taskManager.setStatusChecker(async (taskId: string) => {
  try {
    const response = await Taro.request({
      url: `${API_BASE}/tasks/${taskId}/status`,
      method: 'GET',
      timeout: 10000  // 设置超时
    });

    if (response.statusCode === 200) {
      return response.data.status as TaskStatus;
    }
    
    throw new Error(`HTTP ${response.statusCode}`);
  } catch (error) {
    console.error('状态检查失败:', error);
    throw error;  // 重要：要抛出错误以触发重试机制
  }
});
```

## 📈 最佳实践

1. **合理设置轮询间隔**: 根据任务类型调整，图像处理可以 2-3 秒，数据分析可以 5-10 秒
2. **设置适当超时**: 不要设置过短的超时时间，给任务足够的处理时间
3. **错误分类处理**: 区分网络错误、业务错误、授权错误等，给用户明确提示
4. **用户体验优化**: 在关键状态变化时给用户及时反馈
5. **资源清理**: 页面卸载时记得取消不再需要的任务

## 🤝 贡献

欢迎提交 Issue 和 Pull Request 来帮助改进这个工具！

## 📄 许可证

MIT License