[Node Library] @tool-chain/core
强大的链式异步执行库 - 用优雅的 API 构建复杂的异步工作流。支持 ESM 和 CommonJS 双模块格式,提供完整的 TypeScript 类型安全和灵活的错误处理。
简化异步操作,让你的代码更优雅、更可读、更易维护。
🚀 快速开始
安装
使用你喜欢的包管理器:
# npm
npm install @tool-chain/core
# yarn
yarn add @tool-chain/core
# pnpm
pnpm add @tool-chain/core
环境要求
- Node.js: >= 12.0
- TypeScript: >= 4.5(可选,但推荐用于完整的类型支持)
- 浏览器: 现代浏览器(需配合构建工具如 Webpack/Rollup)
30秒快速示例
库提供两个强大的异步工作流管理工具:
选项1:Chains - 访问全部前面的结果
当你需要访问多个历史结果时使用 Chains:
import { Chains } from '@tool-chain/core';
const result = await new Chains()
.chain(() => 10) // 第1步:返回 10,r1 = 10
.chain((r) => r.r1 * 2) // 第2步:r1=10,返回 20,r2 = 20
.chain((r) => r.r2 + 5) // 第3步:r2=20,返回 25,r3 = 25
.invoke();
console.log(result); // 输出: 25
// 关键概念:
// - 每步可以访问所有前面的结果(r1, r2, r3...)
// - r1 是第1步的结果,r2 是第2步的结果,依此类推
// - .invoke() 执行整个链并返回最后一步的结果
选项2:Tasks - 清晰的任务间参数传递
当你更喜欢基于参数的流程控制时使用 Tasks:
import { Tasks } from '@tool-chain/core';
await new Tasks()
.addTask(async ({ next }) => {
const data = 10;
await next(data * 2); // 向下一个任务传递 20
})
.addTask(async ({ param, next }) => {
// param = 20(来自前一个任务)
await next(param + 5); // 向下一个任务传递 25
})
.addTask(async ({ param }) => {
console.log(param); // 输出: 25
})
.invoke();
就这样!你已经掌握了基础用法。继续阅读了解更多强大功能以及何时使用各个工具。
✨ 核心特性
🔗 两种强大的执行模式
Chains - 多结果访问
- 使用
.chain()方法构建异步工作流 - 支持访问所有历史结果(
r1,r2,r3…) - 完美适配需要多个中间结果的数据管道
Tasks - 参数传递
- 使用
.addTask()方法进行顺序任务执行 - 通过
next(param)进行清晰的任务间参数传递 - 完美适配线性数据流的工作流
🛡️ 灵活的错误处理
- 默认模式:错误直接抛出,中断执行链
- 捕获模式:使用
{ withoutThrow: true }按步骤捕获错误(仅 Chains) - 混合模式:在同一链中灵活混合两种错误处理策略(仅 Chains)
- 任务错误:错误立即传播,停止后续任务(Tasks)
- 细粒度的错误控制,无需 try-catch 包装整个链
🎯 智能重试和超时
- 自动重试失败的步骤(支持自定义重试次数)
- 灵活的重试条件(按错误类型、错误消息或正则表达式)
- 可配置的重试延迟(防止立即重试导致的资源浪费)
- 执行超时控制(防止长时间挂起)
- 注意:重试和超时功能可用于 Chains;Tasks 专注于简洁性
📊 初始数据和状态管理
- Chains:在构造时传入初始数据,作为
r1在链中可用 - Tasks:使用
next(param)进行任务间参数传递 - 自动保留所有步骤的执行结果
- 灵活的数据流和状态管理
✅ 完整的 TypeScript 支持
- 完整的类型定义文件(
.d.ts) - 严格模式编译配置
- 智能的类型推断(Chains 支持 20+ 步链;Tasks 提供完整的类型安全)
- 完美的 IDE 自动补全和类型检查
- Tasks 中的类型安全参数传递确保编译时类型安全
📦 双模块支持
- ESM (ES Modules) - 现代 JavaScript 模块格式
- CommonJS - Node.js 标准模块格式
- 自动模块识别,无需手动配置
📚 详细使用指南
Chains 使用
基础链式调用
创建一个简单的链式流程,逐步处理数据:
import { Chains } from '@tool-chain/core';
const result = await new Chains()
.chain(() => 10)
.chain((r) => r.r1 * 2) // 访问第1步结果
.chain((r) => r.r2 + 5) // 访问第2步结果
.chain((r) => r.r3.toString()) // 转换为字符串
.invoke();
console.log(result); // "25"
关键点:
- 每个
.chain()步骤都可以访问所有之前的结果 r1是第一步的结果,r2是第二步的结果,依此类推- 最后必须调用
.invoke()来执行整个链
工厂函数
使用 createChains() 工厂函数创建实例,代码更简洁:
import { createChains } from '@tool-chain/core';
const result = await createChains()
.chain(() => 'hello')
.chain((r) => r.r1.toUpperCase())
.invoke();
console.log(result); // "HELLO"
使用初始数据
在构造时传入初始数据,它将作为 r1 在链中可用:
const result = await new Chains(100) // 初始数据为 100
.chain((r) => r.r1 * 2) // r1 = 100,结果为 200
.chain((r) => r.r2 - 50) // r2 = 200,结果为 150
.invoke();
console.log(result); // 150
适用场景:
- 处理从函数参数传入的初始数据
- 构建可重用的数据处理流程
- 在类方法中初始化链
错误处理 - 深掘
方式 1:默认抛出模式
错误会直接抛出,中断链的执行。使用 try-catch 处理:
try {
const result = await new Chains()
.chain(() => JSON.parse('invalid')) // 抛出 SyntaxError
.chain((r) => r.r1.name) // 不会执行
.invoke();
} catch (error) {
console.error('链执行失败:', error.message);
// 处理错误
}
优点: 简洁清晰,错误立即中断 缺点: 需要 try-catch 包装整个链
方式 2:捕获模式(推荐)
使用 { withoutThrow: true } 选项,错误被捕获并包装为对象,链继续执行:
const result = await new Chains()
.chain(() => JSON.parse('invalid'), { withoutThrow: true })
// 返回: { error: SyntaxError, data: undefined }
.chain((r) => {
if (r.r1.error) {
console.log('解析失败,使用默认值');
return { name: 'default', age: 0 };
}
return r.r1.data;
})
.chain((r) => {
// r.r1 现在是安全的对象
return r.r1.name.toUpperCase();
})
.invoke();
console.log(result); // "DEFAULT"
返回值结构:
{
data?: T; // 成功时的数据
error?: Error; // 失败时的错误对象
}
优点: 灵活处理错误,链继续执行,可选择降级 缺点: 需要在每个步骤检查错误
方式 3:混合模式
在同一条链中混合使用两种错误处理方式:
const result = await new Chains()
// 第1步:捕获网络错误
.chain(
async () => {
const resp = await fetch('/api/users');
return resp.json();
},
{ withoutThrow: true },
)
// 第2步:检查错误,可选择降级
.chain((r) => {
if (r.r1.error) {
console.log('API 调用失败,使用缓存数据');
return [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
}
return r.r1.data;
})
// 第3步:数据转换(使用默认模式,错误直接抛出)
.chain((r) => {
return r.r1.map((user) => user.name.toUpperCase());
})
// 第4步:再次捕获错误
.chain((r) => saveToDatabase(r.r1), { withoutThrow: true })
.invoke();
关键概念:
- 每个步骤可独立设置错误处理方式
- 捕获模式的错误对象格式统一,便于处理
- 混合使用可以在关键位置防守,在其他位置快速失败
方式 4:条件性错误重试
根据错误类型或消息选择性地重试:
const result = await new Chains()
.chain(
async () => {
const res = await fetch('/api/data');
if (res.status === 429) {
throw new Error('Rate limited');
}
if (res.status >= 500) {
throw new Error('Server error');
}
return res.json();
},
{
retry: 3,
retryWhen: 'Rate limited', // 只在这个错误时重试
retryDelay: 2000,
},
)
.invoke();
智能重试和超时 - 深掘
基本重试
失败时自动重试指定次数:
const result = await new Chains()
.chain(
async () => {
return fetch('/api/unstable').then((r) => r.json());
},
{
retry: 3, // 最多重试3次(总共最多4次请求)
retryDelay: 1000, // 每次重试间隔1秒
},
)
.invoke();
工作流程:
- 执行函数
- 失败 → 等待 retryDelay → 重试
- 重复直到成功或重试次数用尽
智能重试条件
只在特定错误时重试,其他错误立即失败:
const result = await new Chains()
.chain(
async () => {
const res = await fetch('/api/data');
if (res.status === 429) throw new Error('Rate limited');
if (res.status === 404) throw new Error('Not found');
return res.json();
},
{
retry: 5,
// 方式1:字符串 - 错误消息包含此字符串时重试
retryWhen: 'Rate limited',
retryDelay: 2000,
},
)
.invoke();
retryWhen 参数类型:
// 1. Error 类型 - 仅重试该类型的错误
retryWhen: TypeError; // 只重试 TypeError
// 2. 字符串 - 错误消息包含此字符串时重试
retryWhen: 'timeout'; // 错误消息包含 'timeout' 时重试
// 3. 正则表达式 - 错误消息匹配正则时重试
retryWhen: /timeout|Rate limited/; // 匹配这两个词时重试
超时控制
防止请求长时间挂起:
const result = await new Chains()
.chain(
async () => {
return fetch('/api/slow-endpoint').then((r) => r.json());
},
{
timeout: 5000, // 5秒超时,超时后抛出错误
retry: 2, // 如果超时,重试2次
retryWhen: /timeout/, // 只在超时时重试
},
)
.chain((r) => {
// 这里保证数据在5秒内获取
return r.r1;
})
.invoke();
超时错误示例:
// 超时会抛出错误(如果未设置 withoutThrow)
// Error: Timeout exceeded: operation took longer than 5000ms
完整重试配置示例
const result = await new Chains()
.chain(
async () => {
const res = await fetch('/api/critical-data');
if (res.status === 429) throw new Error('Rate limited');
return res.json();
},
{
timeout: 5000, // 单个请求最多等待5秒
retry: 5, // 最多重试5次
retryWhen: /Rate limited|timeout/, // 仅这两类错误重试
retryDelay: 1000, // 重试间隔1秒
withoutThrow: true, // 最终失败不抛出,而是返回错误对象
},
)
.chain((r) => {
if (r.r1.error) {
console.log('获取关键数据失败,可能需要人工介入');
return { cached: true, data: getCachedData() };
}
return r.r1.data;
})
.invoke();
异步操作链
链接多个异步操作,自动处理 Promise:
const result = await new Chains()
// 第1步:获取用户列表
.chain(async () => {
const res = await fetch('/api/users');
return res.json();
})
// 第2步:获取用户详情
.chain(async (r) => {
const userIds = r.r1.map((u) => u.id);
const res = await fetch(`/api/user-details?ids=${userIds.join(',')}`);
return res.json();
})
// 第3步:组合结果
.chain((r) => {
return {
users: r.r1, // 第1步结果
details: r.r2, // 第2步结果
};
})
.invoke();
同步和异步混合
自由混合同步和异步操作:
const result = await new Chains()
.chain(() => 10) // 同步:初始值
.chain(async (r) => {
// 异步:模拟 API 调用
await new Promise((resolve) => setTimeout(resolve, 100));
return r.r1 * 2; // 20
})
.chain((r) => {
// 同步:数据转换
return r.r2 + 5; // 25
})
.chain(async (r) => {
// 异步:保存数据
await saveToDatabase({ value: r.r1 });
return r.r1;
})
.invoke();
console.log(result); // 25
Tasks 使用
Tasks 类提供了一个轻量级的任务顺序执行方案,支持任务间的参数传递。与 Chains 不同,Tasks 专注于清晰的基于参数的流程控制。
基础任务执行
按顺序执行多个任务,每个任务可以调用 next() 将数据传递给下一个任务:
import { Tasks, createTasks } from '@tool-chain/core';
await new Tasks()
.addTask(async ({ next }) => {
console.log('任务 1');
await next(); // 继续下一个任务
})
.addTask(async ({ next }) => {
console.log('任务 2');
await next(42); // 向下一个任务传递参数
})
.addTask<number>(async ({ param }) => {
console.log(`任务 3 收到: ${param}`); // 42
})
.invoke();
任务间参数传递
每个任务都可以从前一个任务接收参数,具有完整的类型安全性:
import { createTasks } from '@tool-chain/core';
await createTasks()
.addTask<string>(async ({ next }) => {
// 这个任务向下一个任务传递一个字符串
await next('hello');
})
.addTask(async ({ param, next }) => {
// param 被推断为 string(非可选类型)
console.log(param); // "hello"
await next({ id: 1, name: 'Alice' });
})
.addTask<{ id: number; name: string }>(async ({ param }) => {
// param 被推断为 { id: number; name: string }
console.log(param.name); // "Alice"
})
.invoke();
使用 finish() 提前终止
可以在任何时刻使用 finish() 方法停止任务执行:
await new Tasks()
.addTask(async ({ next }) => {
console.log('任务 1');
await next();
})
.addTask(async ({ finish }) => {
console.log('任务 2');
await finish(); // 在此停止,不执行任务 3
})
.addTask(async () => {
console.log('任务 3'); // 不会执行
})
.invoke();
自动任务终止
如果任务没有调用 next() 或 finish(),后续任务会自动停止执行:
await new Tasks()
.addTask(async ({ next }) => {
console.log('任务 1');
await next();
})
.addTask(async () => {
console.log('任务 2');
// 没有调用 next() 或 finish() - 自动停止
})
.addTask(async () => {
console.log('任务 3'); // 不会执行
})
.invoke();
Tasks 中的错误处理
任何任务中抛出的错误会停止执行并被传播:
try {
await new Tasks()
.addTask(async ({ next }) => {
console.log('任务 1');
await next();
})
.addTask(async () => {
throw new Error('任务 2 失败');
})
.addTask(async () => {
console.log('任务 3'); // 不会执行
})
.invoke();
} catch (error) {
console.error('错误:', error.message); // "任务 2 失败"
}
工厂函数
使用 createTasks() 可以编写更简洁的代码:
import { createTasks } from '@tool-chain/core';
await createTasks()
.addTask(async ({ next }) => {
await next('数据');
})
.addTask(async ({ param }) => {
console.log(param);
})
.invoke();
🎯 常见使用场景
场景 1:数据处理管道
// 数据验证 → 转换 → 清洗 → 保存
const result = await new Chains(rawUserData)
// 验证数据
.chain((r) => {
const valid = r.r1.every((u) => u.id && u.name && u.email);
if (!valid) throw new Error('Invalid data');
return r.r1;
})
// 数据转换
.chain((r) => {
return r.r1.map((u) => ({
id: u.id,
name: u.name.trim(),
email: u.email.toLowerCase(),
}));
})
// 数据清洗(去除重复)
.chain((r) => {
const seen = new Set();
return r.r1.filter((u) => {
if (seen.has(u.email)) return false;
seen.add(u.email);
return true;
});
})
// 保存到数据库
.chain(async (r) => {
return db.users.insertMany(r.r1);
})
.invoke();
场景 2:API 调用链
// 获取用户 → 获取权限 → 获取配置 → 组合返回
const userData = await new Chains()
// 获取用户信息
.chain(
async () => {
const res = await fetch(`/api/user/${userId}`);
return res.json();
},
{ timeout: 5000 },
)
// 获取用户权限
.chain(
async (r) => {
const res = await fetch(`/api/permissions/${r.r1.id}`);
return res.json();
},
{ timeout: 5000 },
)
// 获取用户配置
.chain(
async (r) => {
const res = await fetch(`/api/config/${r.r1.userId}`);
return res.json();
},
{ timeout: 5000 },
)
// 组合所有数据
.chain((r) => {
return {
user: r.r1,
permissions: r.r2,
config: r.r3,
};
})
.invoke();
场景 3:错误恢复和降级
// 尝试主数据源 → 失败则使用备用源 → 最后使用本地缓存
const data = await new Chains()
// 尝试从 API 获取
.chain(
async () => {
const res = await fetch('/api/primary-source');
if (!res.ok) throw new Error('Primary source failed');
return res.json();
},
{ withoutThrow: true },
)
// 如果失败,尝试备用源
.chain(
async (r) => {
if (r.r1.error) {
console.log('Primary source failed, trying backup...');
const res = await fetch('/api/backup-source');
if (!res.ok) throw new Error('Backup source failed');
return res.json();
}
return r.r1.data;
},
{ withoutThrow: true },
)
// 如果都失败,使用本地缓存
.chain((r) => {
if (r.r2.error) {
console.log('Both sources failed, using local cache');
return getLocalCache();
}
return r.r2.data;
})
.invoke();
场景 4:批量操作重试
// 处理大量数据,重试失败的项
const results = await new Chains(items)
.chain((r) =>
Promise.all(
r.r1.map((item) =>
new Chains(item)
.chain(
async (ir) => {
return processItem(ir.r1);
},
{
retry: 3,
retryDelay: 500,
withoutThrow: true,
},
)
.invoke(),
),
),
)
// 统计成功和失败
.chain((r) => {
const successful = r.r1.filter((result) => !result.error);
const failed = r.r1.filter((result) => result.error);
return {
total: r.r1.length,
successful: successful.length,
failed: failed.length,
results: r.r1,
};
})
.invoke();
场景 5:Web 爬虫
// 获取列表 → 逐个爬取详情 → 去重 → 保存
const scrapedData = await new Chains()
// 获取列表页
.chain(
async () => {
const res = await fetch(listUrl);
const html = await res.text();
const urls = extractUrlsFromHtml(html);
return urls;
},
{ timeout: 10000, retry: 2 },
)
// 爬取每个详情页
.chain(
async (r) => {
const details = await Promise.all(
r.r1.map(async (url) => {
const res = await fetch(url);
const html = await res.text();
return parseDetailPage(html);
}),
);
return details;
},
{ timeout: 30000, retry: 1 },
)
// 去重
.chain((r) => {
const seen = new Set();
return r.r1.filter((item) => {
const key = item.id;
if (seen.has(key)) return false;
seen.add(key);
return true;
});
})
// 保存到数据库
.chain(async (r) => {
return db.items.insertMany(r.r1);
})
.invoke();
📖 完整 API 参考
类:Chains<TResults>
链式执行的主类,支持方法链。
构造函数
constructor(initialData?: T)
参数:
initialData(可选) - 初始数据,将作为第一个结果r1可用
示例:
// 不带初始数据
const chain = new Chains();
// 带初始数据
const chain = new Chains(100);
const chain = new Chains({ name: 'John', age: 30 });
const chain = new Chains([1, 2, 3]);
方法:chain<R>(fn, options?)
添加一个执行步骤到链中。
参数:
fn- 执行函数,签名为(results: ResultsObject) => T | Promise<T>options(可选) - 选项对象,类型为ChainOptions<T>
返回值:
- 返回新的
Chains实例,支持链式调用
示例:
const chain = new Chains()
.chain(() => getValue())
.chain((r) => r.r1 + 10)
.chain(
async (r) => {
const data = await fetchData();
return data;
},
{ timeout: 5000 },
);
方法:invoke()
执行整个链并返回最终结果。
返回值:
Promise<T>- 最后一个 chain 步骤的返回值
示例:
const result = await new Chains()
.chain(() => 10)
.chain((r) => r.r1 * 2)
.invoke();
console.log(result); // 20
函数:createChains<T>(initialData?)
工厂函数,创建一个新的 Chains 实例。
参数:
initialData(可选) - 初始数据
返回值:
Chains<[T]>- Chains 实例
示例:
const result = await createChains(100)
.chain((r) => r.r1 * 2)
.invoke();
console.log(result); // 200
选项对象:ChainOptions<T>
chain() 方法的选项对象。
属性表:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
withoutThrow |
boolean |
false |
是否捕获错误并包装为 { data?: T; error?: Error } 对象,而非直接抛出 |
retry |
number |
0 |
失败时的重试次数(0 表示不重试) |
retryWhen |
Error \| string \| RegExp |
无 | 重试条件:仅当满足条件时才重试 |
retryDelay |
number |
0 |
重试间隔(毫秒) |
timeout |
number |
无 | 执行超时时间(毫秒),超时后抛出 TimeoutError |
详细说明:
withoutThrow
控制错误处理行为:
// false(默认):错误直接抛出
.chain(() => throwError()) // 抛出错误
// true:错误被捕获,返回对象
.chain(() => throwError(), { withoutThrow: true })
// 返回: { error: Error, data: undefined }
retry 和 retryDelay
配置重试行为:
.chain(
() => unreliableOperation(),
{
retry: 3, // 最多重试3次
retryDelay: 1000 // 每次重试等待1秒
}
)
retryWhen
指定重试条件(三种形式):
// 1. 错误类型
.chain(() => operation(), {
retry: 3,
retryWhen: TypeError // 仅重试 TypeError
})
// 2. 字符串匹配
.chain(() => operation(), {
retry: 3,
retryWhen: 'timeout' // 错误消息包含 'timeout' 时重试
})
// 3. 正则表达式
.chain(() => operation(), {
retry: 3,
retryWhen: /timeout|rate limit/ // 匹配这两个词时重试
})
timeout
设置执行超时:
.chain(
async () => slowOperation(),
{
timeout: 5000 // 5秒超时
}
)
// 如果超过5秒:TimeoutError: Timeout exceeded
结果对象格式
每个 .chain() 步骤接收一个结果对象 r,包含所有之前步骤的结果:
{
r1: T1, // 第1步的结果(或初始数据)
r2: T2, // 第2步的结果
r3: T3, // 第3步的结果
...
r20: T20, // 第20步的结果(最多支持20+步)
}
访问结果示例:
await new Chains(10)
.chain((r) => {
console.log(r.r1); // 初始数据:10
return r.r1 * 2; // 20
})
.chain((r) => {
console.log(r.r1); // 第1步结果:10(初始数据)
console.log(r.r2); // 第2步结果:20
return r.r2 + 5; // 25
})
.invoke();
错误对象格式
使用 { withoutThrow: true } 时的返回格式:
// 成功时
{
data: T,
error: undefined
}
// 失败时
{
data: undefined,
error: Error
}
// 类型定义
{
data?: T;
error?: Error;
}
使用示例:
const result = await new Chains()
.chain(() => JSON.parse('invalid'), { withoutThrow: true })
.chain((r) => {
if (r.r1.error) {
console.error('解析失败:', r.r1.error.message);
return null;
}
console.log('解析成功:', r.r1.data);
return r.r1.data;
})
.invoke();
🎓 高级主题
类型推断和 TypeScript
@tool-chain/core 提供完整的 TypeScript 支持,自动推断每个步骤的类型:
const result = await new Chains(10) // r1: number
.chain((r) => r.r1.toString()) // r2: string
.chain((r) => r.r2.length) // r3: number
.chain((r) => ({ value: r.r3 })) // r4: { value: number }
.invoke(); // result: { value: number }
// 完整的类型检查和自动补全
最佳实践:
// ✅ 推荐:直接从初始数据推断类型
const data = await new Chains({ id: 1, name: 'John' })
.chain((r) => r.r1.id)
.invoke();
// 自动推断:data 是 number
// ✅ 推荐:显式类型注解初始数据
const data = await new Chains<User>(fetchUser())
.chain((r) => r.r1.name)
.invoke();
性能优化
1. 避免不必要的中间结果
// ❌ 不优化:创建多个中间结果
const step1 = await new Chains(data).chain(transform1).invoke();
const step2 = await new Chains(step1).chain(transform2).invoke();
const step3 = await new Chains(step2).chain(transform3).invoke();
// ✅ 优化:单个链处理多步
const result = await new Chains(data)
.chain(transform1)
.chain((r) => transform2(r.r1))
.chain((r) => transform3(r.r1))
.invoke();
2. 合理使用并发
// ❌ 序列执行(慢)
const result = await new Chains(ids)
.chain(async (r) => {
const user = await fetchUser(r.r1[0]);
return user;
})
.chain(async (r) => {
const posts = await fetchPosts(r.r1.id);
return posts;
})
.invoke();
// ✅ 并发执行(快)
const result = await new Chains(ids)
.chain(async (r) => {
const [user, posts] = await Promise.all([
fetchUser(r.r1[0]),
fetchPosts(r.r1[0]),
]);
return { user, posts };
})
.invoke();
3. 及时清理大对象
// 对于大数据量,在处理完后清空引用
const result = await new Chains(largeData)
.chain((r) => {
const processed = processLargeData(r.r1);
// 如果后续步骤不需要原始数据,可返回新对象
return processed;
})
.invoke();
常见错误及解决方案
错误 1:忘记 await
// ❌ 错误:返回 Promise,未等待
const result = new Chains()
.chain(() => asyncOperation())
.invoke(); // 返回 Promise
// ✅ 正确:添加 await
const result = await new Chains()
.chain(() => asyncOperation())
.invoke();
错误 2:访问不存在的结果
// ❌ 错误:只有 2 步,却访问 r3
const result = await new Chains()
.chain(() => 10)
.chain((r) => r.r3) // r3 不存在!
.invoke();
// ✅ 正确:只访问已有的结果
const result = await new Chains()
.chain(() => 10)
.chain((r) => r.r1 * 2)
.chain((r) => r.r2 + 5) // 现在可以访问 r2
.invoke();
错误 3:混淆错误对象格式
// ❌ 错误:withoutThrow 返回对象,但当作普通值处理
const result = await new Chains()
.chain(() => JSON.parse('invalid'), { withoutThrow: true })
.chain((r) => r.r1.length) // r.r1 是对象,不是数组!
.invoke();
// ✅ 正确:检查错误对象
const result = await new Chains()
.chain(() => JSON.parse('invalid'), { withoutThrow: true })
.chain((r) => {
if (r.r1.error) {
return 0;
}
return (r.r1.data as any[]).length;
})
.invoke();
错误 4:重试配置不当
// ❌ 问题:所有错误都重试,包括不应该重试的
.chain(() => operation(), {
retry: 10 // 会重试所有错误,可能很慢
})
// ✅ 解决:指定重试条件
.chain(() => operation(), {
retry: 10,
retryWhen: /timeout|rate limit/ // 只重试特定错误
})
📦 模块导入
ESM 导入
import { Chains, createChains } from 'toolchain';
// 或分别导入
import { Chains } from '@tool-chain/core';
import { createChains } from '@tool-chain/core';
CommonJS 导入
const { Chains, createChains } = require('@tool-chain/core');
浏览器使用
对于浏览器环境,需要通过构建工具(如 Webpack、Rollup)进行打包:
// 配置 webpack.config.js
module.exports = {
entry: './src/index.ts',
output: {
filename: 'bundle.js',
},
resolve: {
extensions: ['.ts', '.js'],
},
module: {
rules: [
{
test: /\.ts$/,
use: 'ts-loader',
},
],
},
};
⚙️ 开发和贡献
项目结构
tool-chain-core/
├── src/
│ ├── index.ts # 主入口文件(导出所有模块)
│ ├── chains.ts # Chains 类和 createChains 工厂函数
│ ├── tasks.ts # Tasks 类和 createTasks 工厂函数
│ └── types.ts # 共享类型定义
├── dist/ # 编译输出
│ ├── cjs/ # CommonJS 格式
│ ├── esm/ # ESM 格式
│ └── types/ # TypeScript 定义文件
├── tests/ # 测试文件
│ ├── setup.ts # 测试工具函数
│ ├── tasks.test.ts # Tasks 测试
│ ├── basic-features.test.ts
│ ├── error-handling.test.ts
│ ├── retry-and-timeout.test.ts
│ └── 其他测试文件...
├── package.json
├── tsconfig.json
├── jest.config.js
└── README.zh.md
安装开发依赖
npm install
开发工作流
监视编译:
npm run dev
构建项目:
npm run build
输出包括:
dist/esm/- ES Module 格式dist/cjs/- CommonJS 格式dist/types/- TypeScript 定义文件
运行测试:
npm test
# 详细输出
npm test -- --verbose
# 监视模式
npm test -- --watch
代码检查和格式化:
# ESLint 检查
npm run lint
# Prettier 自动格式化
npm run format
贡献指南
欢迎提交 Pull Request!请遵循以下步骤:
-
Fork 仓库
# 访问 https://github.com/hu-shukang/tool-chain-core # 点击 Fork 按钮 -
创建功能分支
git checkout -b feature/amazing-feature -
修改代码并提交
git add . git commit -m 'feat: add amazing feature' -
推送到分支
git push origin feature/amazing-feature -
开启 Pull Request
- 访问你的 fork 仓库,点击 “New Pull Request”
- 填写 PR 描述和相关信息
提交规范
遵循 Conventional Commits 规范:
():
类型:
feat- 新功能fix- 修复问题refactor- 代码重构test- 添加/修改测试docs- 文档更新chore- 项目工具配置perf- 性能优化
示例:
feat(chain): add chainIf for conditional execution
- Add chainIf method to support conditional step execution
- Maintain backward compatibility with existing API
Closes #123
贡献要求
提交 PR 前请确保:
- ✅ 所有测试通过(
npm test) - ✅ 代码通过 lint 检查(
npm run lint) - ✅ 代码经过格式化(
npm run format) - ✅ 新功能编写了相应的测试
- ✅ 更新了文档(如有必要)
🐛 故障排除
TypeScript 错误
如果遇到类型错误:
npm run build
这会显示详细的编译错误。常见问题:
- “Cannot find module ‘toolchain’” - 确保已运行
npm install - “Property ‘r2’ does not exist on type” - 检查链的步骤数是否足够
测试失败
npm test -- --verbose
常见原因:
- 异步操作未等待完成
- 测试中未调用
.invoke() - 错误处理配置不当
模块导入问题
确保正确导入:
// ✅ 正确
import { Chains, createChains } from 'toolchain';
// ✅ 也正确
const { Chains, createChains } = require('@tool-chain/core');
// ❌ 错误 - 默认导出不存在
import Chains from 'toolchain';
ESM/CommonJS 兼容性:
- 自动检测环境,选择合适的模块格式
- Node.js 会自动根据文件类型选择(
.mjs用 ESM,.cjs用 CommonJS)
性能问题
如果运行缓慢:
- 检查是否有不必要的重试
- 使用并发而非序列执行
- 减少链的步骤数(合并相邻步骤)
📚 相关资源
📄 许可证
MIT License - 详见 LICENSE 文件
🤝 支持和反馈
- GitHub 仓库 - hu-shukang/toolchain_core
- 问题报告 - 提交 Issue
- 讨论区 - GitHub Discussions
- 作者 - HU SHUKANG
如果你觉得这个库有帮助,欢迎 ⭐ Star 支持!
更新日志
详见 CHANGELOG.md 了解版本历史和更新内容。
最后更新:v1.1.2 | GitHub 仓库