Batching and Caching by andrueandersoncs/claude-skill-effect-ts
npx skills add https://github.com/andrueandersoncs/claude-skill-effect-ts --skill 'Batching and Caching'Effect 为 API 调用提供自动优化:
这自动解决了 N+1 查询问题。
const program = Effect.gen(function* () {
const todos = yield* getTodos()
const owners = yield* Effect.forEach(
todos,
(todo) => getUserById(todo.ownerId),
{ concurrency: "unbounded" }
)
})
Effect 的批处理功能将此转换为优化的批量调用。
import { Request } from "effect"
// 定义请求结构
interface GetUserById extends Request.Request<User, UserNotFound> {
readonly _tag: "GetUserById"
readonly id: number
}
// 创建带标签的构造函数
const GetUserById = Request.tagged<GetUserById>("GetUserById")
import { RequestResolver, Effect } from "effect"
// 批量解析器 - 一次性处理多个请求
const GetUserByIdResolver = RequestResolver.makeBatched(
(requests: ReadonlyArray<GetUserById>) =>
Effect.gen(function* () {
// 单次批量 API 调用
const users = yield* Effect.tryPromise(() =>
fetch("/api/users/batch", {
method: "POST",
body: JSON.stringify({ ids: requests.map((r) => r.id) })
}).then((res) => res.json())
)
// 用结果完成每个请求
yield* Effect.forEach(requests, (request, index) =>
Request.completeEffect(
request,
Effect.succeed(users[index])
)
)
})
)
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
const getUserById = (id: number) =>
Effect.request(GetUserById({ id }), GetUserByIdResolver)
const program = Effect.gen(function* () {
const todos = yield* getTodos()
const owners = yield* Effect.forEach(
todos,
(todo) => getUserById(todo.ownerId),
{ concurrency: "unbounded" }
)
})
const SingleUserResolver = RequestResolver.fromEffect(
(request: GetUserById) =>
Effect.tryPromise(() =>
fetch(`/api/users/${request.id}`).then((r) => r.json())
)
)
const BatchedUserResolver = RequestResolver.makeBatched(
(requests: ReadonlyArray<GetUserById>) =>
// 在一次调用中处理所有请求
batchFetch(requests)
)
const UserResolverWithContext = RequestResolver.makeBatched(
(requests: ReadonlyArray<GetUserById>) =>
Effect.gen(function* () {
// 从上下文中访问服务
const httpClient = yield* HttpClient
const logger = yield* Logger
yield* logger.info(`批处理 ${requests.length} 个用户请求`)
return yield* httpClient.post("/api/users/batch", {
ids: requests.map((r) => r.id)
})
})
)
// 为解析器提供上下文
const ContextualResolver = UserResolverWithContext.pipe(
RequestResolver.provideContext(context)
)
import { Effect } from "effect"
const fetchConfig = Effect.promise(() =>
fetch("/api/config").then((r) => r.json())
)
const cachedConfig = yield* Effect.cached(fetchConfig)
const config1 = yield* cachedConfig
const config2 = yield* cachedConfig
const cachedUser = yield* Effect.cachedWithTTL(
fetchCurrentUser,
"5 minutes"
)
const user1 = yield* cachedUser
yield* Effect.sleep("6 minutes")
const user2 = yield* cachedUser
const [cachedUser, invalidate] = yield* Effect.cachedInvalidateWithTTL(
fetchCurrentUser,
"5 minutes"
)
const user = yield* cachedUser
yield* invalidate
const freshUser = yield* cachedUser
如需更多控制,使用缓存服务:
import { Cache } from "effect"
const program = Effect.gen(function* () {
const cache = yield* Cache.make({
capacity: 100,
timeToLive: "10 minutes",
lookup: (userId: string) => fetchUser(userId)
})
const user1 = yield* cache.get("user-1")
const user2 = yield* cache.get("user-1")
const isCached = yield* cache.contains("user-1")
yield* cache.invalidate("user-1")
const stats = yield* cache.cacheStats
})
请求在查询上下文中自动缓存:
const program = Effect.gen(function* () {
const user1 = yield* getUserById(1)
const user2 = yield* getUserById(1)
const user3 = yield* getUserById(2)
})
const noCaching = getUserById(1).pipe(
Effect.withRequestCaching(false)
)
const customCache = yield* Request.makeCache({
capacity: 1000,
timeToLive: "30 minutes"
})
const program = getUserById(1).pipe(
Effect.withRequestCache(customCache)
)
const noBatching = program.pipe(
Effect.withRequestBatching(false)
)
import { Effect, Request, RequestResolver, Schema } from "effect"
// 错误类型
class UserNotFound extends Schema.TaggedError<UserNotFound>()(
"UserNotFound",
{ id: Schema.Number }
) {}
// 请求类型
interface GetUserById extends Request.Request<User, UserNotFound> {
readonly _tag: "GetUserById"
readonly id: number
}
const GetUserById = Request.tagged<GetUserById>("GetUserById")
// 批量解析器
const UserResolver = RequestResolver.makeBatched(
(requests: ReadonlyArray<GetUserById>) =>
Effect.gen(function* () {
const ids = requests.map((r) => r.id)
const response = yield* Effect.tryPromise({
try: () =>
fetch("/api/users/batch", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ids })
}).then((r) => r.json() as Promise<User[]>),
catch: () => new Error("批量获取失败")
})
yield* Effect.forEach(requests, (request, index) => {
const user = response[index]
return user
? Request.completeEffect(request, Effect.succeed(user))
: Request.completeEffect(
request,
Effect.fail(new UserNotFound({ id: request.id }))
)
})
})
)
// 查询函数
const getUserById = (id: number) =>
Effect.request(GetUserById({ id }), UserResolver)
// 使用 - 自动批处理
const program = Effect.gen(function* () {
const todos = yield* getTodos()
const owners = yield* Effect.forEach(
todos,
(todo) => getUserById(todo.ownerId),
{ concurrency: "unbounded" }
)
return owners
})
如需全面的批处理和缓存文档,请查阅 ${CLAUDE_PLUGIN_ROOT}/references/llms-full.txt。
搜索以下部分:
每周安装量
0
代码仓库
GitHub 星标数
5
首次出现
1970年1月1日
安全审计
Effect provides automatic optimization for API calls:
This solves the N+1 query problem automatically.
const program = Effect.gen(function* () {
const todos = yield* getTodos()
const owners = yield* Effect.forEach(
todos,
(todo) => getUserById(todo.ownerId),
{ concurrency: "unbounded" }
)
})
Effect's batching transforms this into optimized batch calls.
import { Request } from "effect"
// Define request shape
interface GetUserById extends Request.Request<User, UserNotFound> {
readonly _tag: "GetUserById"
readonly id: number
}
// Create tagged constructor
const GetUserById = Request.tagged<GetUserById>("GetUserById")
import { RequestResolver, Effect } from "effect"
// Batched resolver - handles multiple requests at once
const GetUserByIdResolver = RequestResolver.makeBatched(
(requests: ReadonlyArray<GetUserById>) =>
Effect.gen(function* () {
// Single batch API call
const users = yield* Effect.tryPromise(() =>
fetch("/api/users/batch", {
method: "POST",
body: JSON.stringify({ ids: requests.map((r) => r.id) })
}).then((res) => res.json())
)
// Complete each request with its result
yield* Effect.forEach(requests, (request, index) =>
Request.completeEffect(
request,
Effect.succeed(users[index])
)
)
})
)
const getUserById = (id: number) =>
Effect.request(GetUserById({ id }), GetUserByIdResolver)
const program = Effect.gen(function* () {
const todos = yield* getTodos()
const owners = yield* Effect.forEach(
todos,
(todo) => getUserById(todo.ownerId),
{ concurrency: "unbounded" }
)
})
const SingleUserResolver = RequestResolver.fromEffect(
(request: GetUserById) =>
Effect.tryPromise(() =>
fetch(`/api/users/${request.id}`).then((r) => r.json())
)
)
const BatchedUserResolver = RequestResolver.makeBatched(
(requests: ReadonlyArray<GetUserById>) =>
// Handle all requests in one call
batchFetch(requests)
)
const UserResolverWithContext = RequestResolver.makeBatched(
(requests: ReadonlyArray<GetUserById>) =>
Effect.gen(function* () {
// Access services from context
const httpClient = yield* HttpClient
const logger = yield* Logger
yield* logger.info(`Batching ${requests.length} user requests`)
return yield* httpClient.post("/api/users/batch", {
ids: requests.map((r) => r.id)
})
})
)
// Provide context to resolver
const ContextualResolver = UserResolverWithContext.pipe(
RequestResolver.provideContext(context)
)
import { Effect } from "effect"
const fetchConfig = Effect.promise(() =>
fetch("/api/config").then((r) => r.json())
)
const cachedConfig = yield* Effect.cached(fetchConfig)
const config1 = yield* cachedConfig
const config2 = yield* cachedConfig
const cachedUser = yield* Effect.cachedWithTTL(
fetchCurrentUser,
"5 minutes"
)
const user1 = yield* cachedUser
yield* Effect.sleep("6 minutes")
const user2 = yield* cachedUser
const [cachedUser, invalidate] = yield* Effect.cachedInvalidateWithTTL(
fetchCurrentUser,
"5 minutes"
)
const user = yield* cachedUser
yield* invalidate
const freshUser = yield* cachedUser
For more control, use the Cache service:
import { Cache } from "effect"
const program = Effect.gen(function* () {
const cache = yield* Cache.make({
capacity: 100,
timeToLive: "10 minutes",
lookup: (userId: string) => fetchUser(userId)
})
const user1 = yield* cache.get("user-1")
const user2 = yield* cache.get("user-1")
const isCached = yield* cache.contains("user-1")
yield* cache.invalidate("user-1")
const stats = yield* cache.cacheStats
})
Requests are automatically cached within a query context:
const program = Effect.gen(function* () {
const user1 = yield* getUserById(1)
const user2 = yield* getUserById(1)
const user3 = yield* getUserById(2)
})
const noCaching = getUserById(1).pipe(
Effect.withRequestCaching(false)
)
const customCache = yield* Request.makeCache({
capacity: 1000,
timeToLive: "30 minutes"
})
const program = getUserById(1).pipe(
Effect.withRequestCache(customCache)
)
const noBatching = program.pipe(
Effect.withRequestBatching(false)
)
import { Effect, Request, RequestResolver, Schema } from "effect"
// Error types
class UserNotFound extends Schema.TaggedError<UserNotFound>()(
"UserNotFound",
{ id: Schema.Number }
) {}
// Request type
interface GetUserById extends Request.Request<User, UserNotFound> {
readonly _tag: "GetUserById"
readonly id: number
}
const GetUserById = Request.tagged<GetUserById>("GetUserById")
// Batched resolver
const UserResolver = RequestResolver.makeBatched(
(requests: ReadonlyArray<GetUserById>) =>
Effect.gen(function* () {
const ids = requests.map((r) => r.id)
const response = yield* Effect.tryPromise({
try: () =>
fetch("/api/users/batch", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ids })
}).then((r) => r.json() as Promise<User[]>),
catch: () => new Error("Batch fetch failed")
})
yield* Effect.forEach(requests, (request, index) => {
const user = response[index]
return user
? Request.completeEffect(request, Effect.succeed(user))
: Request.completeEffect(
request,
Effect.fail(new UserNotFound({ id: request.id }))
)
})
})
)
// Query function
const getUserById = (id: number) =>
Effect.request(GetUserById({ id }), UserResolver)
// Usage - automatically batched
const program = Effect.gen(function* () {
const todos = yield* getTodos()
const owners = yield* Effect.forEach(
todos,
(todo) => getUserById(todo.ownerId),
{ concurrency: "unbounded" }
)
return owners
})
For comprehensive batching and caching documentation, consult ${CLAUDE_PLUGIN_ROOT}/references/llms-full.txt.
Search for these sections:
Weekly Installs
0
Repository
GitHub Stars
5
First Seen
Jan 1, 1970
Security Audits
GSAP React 动画库使用指南:useGSAP Hook 与最佳实践
2,400 周安装
Conventional Commit规范提交助手 - GitHub Copilot自动生成标准化提交信息
8,500 周安装
SQL性能优化助手 - 专业SQL查询调优与索引策略优化工具
8,600 周安装
Vue Router 最佳实践指南:导航守卫、路由生命周期与常见陷阱解决方案
9,200 周安装
后台代理状态通知 - 避免轮询浪费,高效处理AI代理进度更新
205 周安装
网站设计审查工具 - 自动检测并修复HTML/CSS/JS、React、Vue等框架的视觉与布局问题
8,900 周安装
.NET/C# 最佳实践指南:代码规范、设计模式、依赖注入与AI集成
8,900 周安装