Skip to content

To 工具类文档 - 安全异步执行工具

概述

to.ts 是一个强大的异步执行工具函数集,专门用于将 Promise 和可能抛出异常的代码转换为 [error, data] 格式,避免频繁使用 try-catch,让代码更加简洁和可读。

核心理念

  • 统一错误处理:所有异步操作都返回 [Error | null, T | null] 格式
  • 避免 try-catch 地狱:不再需要到处写 try-catch 代码块
  • 类型安全:完整的 TypeScript 支持
  • 功能丰富:涵盖常见的异步操作场景

核心函数

1. to() - 基础异步处理 ⭐⭐⭐⭐⭐

最重要的函数,99% 的场景都会用到

typescript
const to = async <T>(promise: Promise<T>): Promise<[Error | null, T | null]>

功能描述

将任何 Promise 转换为 [error, data] 格式,避免使用 try-catch。

使用示例

typescript
// ❌ 传统写法
try {
  const user = await getUserById('123');
  console.log('用户信息:', user);
} catch (error) {
  console.error('获取用户失败:', error.message);
  return;
}

// ✅ 使用 to() 函数
const [err, user] = await to(getUserById('123'));
if (err) {
  console.error('获取用户失败:', err.message);
  return;
}
console.log('用户信息:', user);
typescript
// API 请求处理
const [err, response] = await to(fetch('/api/data'));
if (err) {
  showErrorMessage('网络请求失败');
  return;
}

const [parseErr, data] = await to(response.json());
if (parseErr) {
  showErrorMessage('数据解析失败');
  return;
}
console.log('请求结果:', data);

2. toValidate() - 表单验证专用 ⭐⭐⭐⭐

后台管理系统表单验证的利器

typescript
const toValidate = async (formRef: Ref<ElFormInstance>): Promise<[Error | null, boolean]>

功能描述

专门处理 Element Plus 等 UI 库的表单验证,将回调式 API 转换为 Promise。

使用示例

typescript
// Vue 组件中的表单验证
const handleSubmit = async () => {
  const [err, isValid] = await toValidate(formRef.value);
  if (err || !isValid) {
    ElMessage.error(err?.message || '表单验证失败');
    return;
  }

  // 验证通过,提交表单
  const [submitErr] = await to(submitForm(formData));
  if (submitErr) {
    ElMessage.error('提交失败: ' + submitErr.message);
    return;
  }

  ElMessage.success('提交成功');
};
typescript
// 多个表单同时验证
const validateAllForms = async () => {
  const results = await toAll([
    toValidate(userFormRef.value),
    toValidate(addressFormRef.value),
    toValidate(paymentFormRef.value)
  ]);

  const hasErrors = results.some(([err, isValid]) => err || !isValid);
  if (hasErrors) {
    ElMessage.error('请检查表单输入');
    return false;
  }

  return true;
};

3. toAll() - 批量处理 Promise ⭐⭐⭐⭐

并行执行多个异步操作

typescript
const toAll = async <T>(
  promises: (Promise<T> | Promise<[Error | null, T | null]>)[]
): Promise<Array<[Error | null, T | null]>>

功能描述

并行执行多个 Promise,每个都使用 to 函数包装,不会因为某个失败而中断其他请求。支持混合传入原始 Promise 和已用 to() 包装的 Promise。

使用示例

typescript
// 并行请求多个接口
const promises = [
  getUserInfo(userId),              // 原始 Promise
  to(getUserOrders(userId)),        // 手动用 to() 包装的 Promise
  getUserPreferences(userId)        // 原始 Promise
];

const results = await toAll(promises);
const [userErr, userInfo] = results[0];
const [ordersErr, orders] = results[1];
const [prefsErr, preferences] = results[2];

// 统一处理,无论原始格式如何
if (userErr) console.error('获取用户信息失败:', userErr.message);
if (ordersErr) console.error('获取订单失败:', ordersErr.message);
if (prefsErr) console.error('获取偏好设置失败:', prefsErr.message);
typescript
// 批量上传文件
const uploadPromises = files.map(file => uploadFile(file));
const results = await toAll(uploadPromises);

const successCount = results.filter(([err]) => !err).length;
const failCount = results.filter(([err]) => err).length;

console.log(`上传完成: 成功 ${successCount} 个,失败 ${failCount} 个`);

4. toWithTimeout() - 超时控制 ⭐⭐⭐

为 Promise 添加超时机制

typescript
const toWithTimeout = async <T>(
  promise: Promise<T>,
  timeoutMs: number,
  timeoutMessage?: string
): Promise<[Error | null, T | null]>

功能描述

为 Promise 添加超时控制,避免长时间等待,适用于网络请求、外部服务调用等场景。

使用示例

typescript
// 为 API 请求设置 5 秒超时
const [err, data] = await toWithTimeout(
  fetch('/api/heavy-computation'),
  5000,
  '计算超时,请稍后重试'
);

if (err) {
  if (err.message.includes('超时')) {
    showTimeoutMessage();
  } else {
    showErrorMessage('请求失败');
  }
  return;
}
console.log('计算结果:', data);

5. toSync() - 同步版本 ⭐⭐⭐

处理可能抛出异常的同步代码

typescript
const toSync = <T>(fn: () => T): [Error | null, T | null]

功能描述

用于包装可能抛出异常的同步函数,统一错误处理格式。主要用于 JSON 解析、数据处理等可能出错的同步操作。

使用示例

typescript
// 处理 JSON 解析
const [err, data] = toSync(() => JSON.parse(jsonString));
if (err) {
  console.error('JSON 解析失败:', err.message);
  return;
}
console.log('解析结果:', data);
typescript
// 处理数组操作
const [err, result] = toSync(() => {
  if (array.length === 0) throw new Error('数组为空');
  return array[0].someProperty.toUpperCase();
});
if (err) {
  console.error('操作失败:', err.message);
  return;
}
console.log('处理结果:', result);

增强功能函数

6. toWithRetry() - 自动重试 ⭐⭐⭐

适用于网络不稳定场景

typescript
const toWithRetry = async <T>(
  promiseFactory: (() => Promise<T>) | (() => Promise<[Error | null, T | null]>),
  maxRetries: number = 2,
  retryDelay: number = 1000
): Promise<[Error | null, T | null]>

使用示例

typescript
// 重试网络请求
const [err, data] = await toWithRetry(
  () => fetch('/api/unstable-endpoint'),
  3,    // 重试 3 次
  2000  // 间隔 2 秒
);
if (err) {
  console.error('重试 3 次后仍然失败:', err.message);
  showErrorMessage('网络连接不稳定,请稍后重试');
  return;
}
console.log('请求成功:', data);

7. toWithDefault() - 带默认值 ⭐⭐

失败时使用备用数据

typescript
const toWithDefault = async <T>(
  promise: Promise<T>, 
  defaultValue: T
): Promise<[Error | null, T]>

使用示例

typescript
// 获取用户配置,失败时使用默认配置
const [err, config] = await toWithDefault(
  getUserConfig(userId),
  { theme: 'light', language: 'zh-CN', pageSize: 10 }
);
// config 保证不为 null,要么是获取到的配置,要么是默认配置
applyUserConfig(config);
if (err) {
  console.warn('使用默认配置,原因:', err.message);
}

8. toWithLog() - 带调试日志 ⭐⭐

开发阶段监控异步操作

typescript
const toWithLog = async <T>(
  promise: Promise<T>,
  label: string = 'Promise',
  enableLog: boolean = true
): Promise<[Error | null, T | null]>

使用示例

typescript
// 监控 API 请求
const [err, userData] = await toWithLog(
  fetchUserData(userId),
  '获取用户数据',
  true
);
// 控制台输出:
// [获取用户数据] 开始执行
// [获取用户数据] 执行成功 (1234ms)  或  执行失败: 错误信息

高级功能函数

9. toSequence() - 串行执行 ⭐⭐

按顺序处理有依赖关系的操作

typescript
const toSequence = async <T>(
  operations: Array<(() => Promise<T>) | (() => Promise<[Error | null, T | null]>)>
): Promise<[Error | null, T[]]>

使用示例

typescript
// 按顺序执行初始化步骤
const initSteps = [
  () => connectToDatabase(),           // 原始 Promise 函数
  () => to(loadConfiguration()),       // 用 to() 包装的函数
  () => startServices(),               // 原始 Promise 函数
  () => to(scheduleJobs())            // 用 to() 包装的函数
];

const [err, results] = await toSequence(initSteps);
if (err) {
  console.error('初始化失败:', err.message);
  await rollbackInitialization();
  return;
}

console.log('所有初始化步骤完成:', results);

10. toIf() - 条件执行 ⭐

根据条件决定是否执行异步操作

typescript
const toIf = async <T>(
  condition: boolean,
  promise: Promise<T> | Promise<[Error | null, T | null]>
): Promise<[Error | null, T | null]>

使用示例

typescript
// 根据权限决定是否加载敏感数据
const [err, sensitiveData] = await toIf(
  userRole === 'admin',
  fetchSensitiveUserData(userId)
);
if (err) {
  console.error('加载敏感数据失败:', err.message);
} else if (sensitiveData) {
  renderSensitiveDataPanel(sensitiveData);
} else {
  console.log('权限不足,跳过敏感数据加载');
}

11. toResult() - 类型安全透传 ⭐

用于已返回 Result 格式的函数

typescript
const toResult = async <T>(
  resultPromise: Promise<[Error | null, T | null]>
): Promise<[Error | null, T | null]>

使用示例

typescript
// 用于保持 API 一致性
const fetchUserSafely = async (userId: string) => {
  // 这个函数已经返回 Result 格式
  return toResult(apiClient.getUser(userId));
};

// 使用时保持一致的调用方式
const [err, user] = await fetchUserSafely('123');

实用技巧与最佳实践

1. 错误处理策略

typescript
// ✅ 推荐:明确的错误处理
const [err, user] = await to(fetchUser(id));
if (err) {
  // 根据错误类型采取不同处理策略
  if (err.message.includes('网络')) {
    showNetworkError();
  } else if (err.message.includes('权限')) {
    redirectToLogin();
  } else {
    showGenericError();
  }
  return;
}
// 使用 user 数据

2. 嵌套调用处理

typescript
// ✅ 推荐:扁平化处理
const [userErr, user] = await to(fetchUser(id));
if (userErr) return handleError(userErr);

const [orderErr, orders] = await to(fetchUserOrders(user.id));
if (orderErr) return handleError(orderErr);

const [prefErr, preferences] = await to(fetchUserPreferences(user.id));
if (prefErr) return handleError(prefErr);

// 所有数据都成功获取
renderUserDashboard(user, orders, preferences);

3. 表单验证组合

typescript
// ✅ 推荐:组合多种验证
const validateAndSubmit = async () => {
  // 1. 表单验证
  const [validateErr, isValid] = await toValidate(formRef.value);
  if (validateErr || !isValid) {
    showValidationErrors(validateErr?.message);
    return;
  }

  // 2. 提交数据
  const [submitErr, response] = await to(submitForm(formData));
  if (submitErr) {
    showSubmitError(submitErr.message);
    return;
  }

  // 3. 成功处理
  showSuccessMessage('提交成功');
  router.push('/success');
};

4. 并行请求优化

typescript
// ✅ 推荐:合理使用并行请求
const loadDashboardData = async () => {
  const results = await toAll([
    fetchUserInfo(),
    fetchNotifications(),
    fetchRecentActivity(),
    fetchSystemStats()
  ]);

  const [userErr, userInfo] = results[0];
  const [notifyErr, notifications] = results[1];
  const [activityErr, activities] = results[2];
  const [statsErr, stats] = results[3];

  // 部分数据失败不影响整体页面渲染
  if (!userErr) setUserInfo(userInfo);
  if (!notifyErr) setNotifications(notifications);
  if (!activityErr) setActivities(activities);
  if (!statsErr) setStats(stats);
};

常见场景示例

场景 1: 用户登录流程

typescript
const handleLogin = async () => {
  // 1. 表单验证
  const [validateErr, isValid] = await toValidate(loginFormRef.value);
  if (validateErr || !isValid) {
    ElMessage.error('请检查输入信息');
    return;
  }

  // 2. 登录请求(带超时)
  showLoading('正在登录...');
  const [loginErr, loginResult] = await toWithTimeout(
    userLogin(loginForm),
    10000,
    '登录超时,请重试'
  );
  hideLoading();

  if (loginErr) {
    ElMessage.error(`登录失败: ${loginErr.message}`);
    return;
  }

  // 3. 获取用户信息
  const [userErr, userInfo] = await to(fetchUserInfo());
  if (userErr) {
    ElMessage.warning('登录成功,但获取用户信息失败');
  } else {
    setUserInfo(userInfo);
  }

  ElMessage.success('登录成功');
  router.push('/dashboard');
};

场景 2: 文件上传处理

typescript
const handleFileUpload = async (files: File[]) => {
  // 并行上传多个文件
  const uploadPromises = files.map(file => 
    toWithRetry(() => uploadFile(file), 3, 1000)
  );
  
  const results = await toAll(uploadPromises);
  
  const successFiles = [];
  const failedFiles = [];
  
  results.forEach(([err, result], index) => {
    if (err) {
      failedFiles.push({ file: files[index], error: err.message });
    } else {
      successFiles.push(result);
    }
  });
  
  if (successFiles.length > 0) {
    ElMessage.success(`成功上传 ${successFiles.length} 个文件`);
  }
  
  if (failedFiles.length > 0) {
    ElMessage.error(`${failedFiles.length} 个文件上传失败`);
    console.error('上传失败的文件:', failedFiles);
  }
};

场景 3: 数据初始化流程

typescript
const initializeApp = async () => {
  const initSteps = [
    () => to(checkSystemHealth()),
    () => to(loadApplicationConfig()),
    () => to(initializeDatabase()),
    () => to(setupEventListeners()),
    () => to(loadUserPreferences())
  ];

  const [err, results] = await toSequence(initSteps);
  
  if (err) {
    console.error('应用初始化失败:', err.message);
    showErrorDialog('系统初始化失败,请刷新页面重试');
    return;
  }

  console.log('应用初始化完成,执行了', results.length, '个步骤');
  setAppReady(true);
};

TypeScript 类型说明

Result 类型

typescript
type Result<T> = [Error | null, T | null];

常用类型示例

typescript
// API 响应类型
interface ApiResponse<T> {
  data: T;
  message: string;
  code: number;
}

// 使用示例
const [err, response] = await to<ApiResponse<User>>(fetchUser(id));
if (!err && response) {
  const user: User = response.data;
}

性能优化建议

1. 避免不必要的包装

typescript
// ❌ 不推荐:已经是 to 格式的函数重复包装
const result = await to(alreadyToWrappedFunction()); // 重复包装

// ✅ 推荐:使用 toResult 或直接调用
const result = await toResult(alreadyToWrappedFunction());
// 或者
const result = await alreadyToWrappedFunction();

2. 合理使用重试机制

typescript
// ❌ 不推荐:对所有请求都使用重试
const [err, data] = await toWithRetry(() => fetchData(), 5, 1000);

// ✅ 推荐:只对不稳定的请求使用重试
const [err, data] = await toWithRetry(() => fetchUnstableData(), 2, 500);

3. 批量请求性能

typescript
// ❌ 不推荐:串行请求
const user = await to(fetchUser());
const orders = await to(fetchOrders());
const preferences = await to(fetchPreferences());

// ✅ 推荐:并行请求
const [userResult, ordersResult, prefsResult] = await toAll([
  fetchUser(),
  fetchOrders(), 
  fetchPreferences()
]);

总结

to.ts 工具类提供了完整的异步错误处理解决方案:

  • 核心功能to()toValidate()toAll() 覆盖 90% 的使用场景
  • 增强功能:超时、重试、默认值等高级特性
  • 开发友好:完整的 TypeScript 支持和清晰的 API 设计
  • 性能优化:支持并行、串行、条件执行等多种模式

通过使用这些工具函数,可以显著提高代码的可读性、可维护性和健壮性,是现代前端开发中处理异步操作的最佳实践。