front-cpu
v0.1.3
Published
FrontCPU - Frontend instruction execution system with out-of-order execution
Maintainers
Readme
FrontCPU
前端异步的困境
每个前端开发者都经历过这些噩梦:
调试地狱
// 你的代码里到处都是这种东西
console.log('开始请求...')
console.log('请求参数:', params)
console.log('请求返回:', response)
console.log('这里执行了吗?')
console.log('为什么这里没执行???')
console.log('fuck')竞态条件
// 用户快速切换标签页
fetchTabData('tab1') // 请求发出,耗时 500ms
fetchTabData('tab2') // 请求发出,耗时 200ms
fetchTabData('tab3') // 请求发出,耗时 300ms
// 结果:tab2 的数据先回来,然后 tab3,最后 tab1
// 用户看到的是 tab1 的数据,但他明明点的是 tab3请求混乱
// 同时修改同一条数据
updateUser({ id: 1, name: 'Alice' }) // 请求 A
updateUser({ id: 1, name: 'Bob' }) // 请求 B
// 请求 B 先到达服务器,请求 A 后到达
// 数据库里是 Alice,但用户以为是 Bob可观测性为零
- 请求发出去了吗?
- 卡在哪个环节?
- 为什么这个请求比那个慢?
- 乐观更新回滚了吗?
- 这个 bug 到底怎么复现?
解决方案:像 CPU 一样思考
现代 CPU 不会傻傻地一条一条执行指令。它会:
- 分析指令之间的依赖关系
- 没有冲突的指令并行执行
- 有冲突的指令自动排队
- 全程追踪每条指令的状态
你的前端应用也应该这样。
FrontCPU 将真实 CPU 的乱序执行架构带到前端,让你的异步操作:
- 自动检测资源冲突
- 自动决定并行还是串行
- 自动追踪每个操作的完整生命周期
- 零代码实现顶级调试体验
调试体验对比
以前:console.log 地狱
async function updateTask(id, data) {
console.log('[updateTask] 开始', { id, data })
try {
console.log('[updateTask] 发送请求...')
const response = await fetch(`/api/tasks/${id}`, {
method: 'PUT',
body: JSON.stringify(data)
})
console.log('[updateTask] 响应状态:', response.status)
const result = await response.json()
console.log('[updateTask] 返回数据:', result)
store.tasks[id] = result
console.log('[updateTask] 状态已更新')
return result
} catch (error) {
console.error('[updateTask] 失败:', error)
throw error
}
}控制台输出:
[updateTask] 开始 {id: 1, data: {name: 'test'}}
[updateTask] 发送请求...
[updateTask] 响应状态: 200
[updateTask] 返回数据: {id: 1, name: 'test'}
[updateTask] 状态已更新问题:
- 手写大量 log 代码
- 不知道请求耗时多少
- 不知道代码从哪里调用的
- 多个请求的 log 混在一起
- 上线前还要删掉这些 log
现在:零代码,顶级调试
// 定义指令,一行 log 都不用写
registerISA({
'task.update': {
meta: {
description: '更新任务',
resourceIdentifier: (p) => [`task:${p.id}`],
},
request: {
method: 'PUT',
url: (p) => `/api/tasks/${p.id}`,
body: (p) => p.data,
},
commit: async (result) => {
store.tasks[result.id] = result
},
},
})
// 使用
pipeline.dispatch('task.update', { id: 1, data: { name: 'test' } })控制台自动输出:
[指令创建] 14:32:15.123 task.update 📍 src/views/TaskList.vue:42
├─ Instruction ID: instr-1760629698922-11
├─ Correlation ID: corr_1760629698922_f_M_sBit6A
└─ Payload: { id: 1, data: { name: 'test' } }
[指令成功] 14:32:15.456 task.update 333ms 📍 src/views/TaskList.vue:42
├─ 📝 指令参数: { id: 1, data: { name: 'test' } }
├─ 📥 后端返回: { id: 1, name: 'test', updatedAt: '...' }
├─ 💾 WB阶段: commit() 执行成功
└─ 流水线阶段:
IF→SCH 0ms
SCH→EX 2ms
EX→WB ██████ 331ms
总耗时: 333ms一行调试代码都不用写,自动获得:
- 精确到毫秒的时间戳
- 调用源文件和行号(点击可跳转)
- 请求参数和返回值
- 每个流水线阶段的耗时可视化
- 唯一的指令 ID 和关联 ID(用于追踪)
失败时自动展开,智能建议
[指令失败] 14:32:16.789 task.update 1502ms 📍 src/views/TaskList.vue:42
├─ 原因: Request timeout
├─ 📝 指令参数: { id: 1, data: { name: 'test' } }
├─ 🔄 已回滚乐观更新
├─ 流水线阶段:
│ IF→SCH 0ms
│ SCH→EX 1ms
│ EX→WB ████████████████████ 1501ms
│ 总耗时: 1502ms
├─ 💡 建议:
│ • 请求超时,考虑增加超时时间或优化后端性能
│ • 执行耗时 1502ms,超过 1 秒,检查是否存在性能问题
└─ Error Stack: ...新的开发范式
使用 FrontCPU,你只需要做四件事:
1. UI 层:保持不变
你之前用什么框架写 UI,现在就还怎么写。Vue、React、Svelte 都行。
<template>
<div v-for="task in tasks" :key="task.id">
{{ task.name }}
<button @click="completeTask(task.id)">完成</button>
</div>
</template>2. 定义指令集
声明每个操作:做什么、怎么调接口、怎么更新状态、是否需要排队。
registerISA({
'task.complete': {
meta: {
description: '完成任务',
category: 'task',
resourceIdentifier: (p) => [`task:${p.id}`], // 资源标识,用于冲突检测
schedulingStrategy: 'serial', // 同一任务的操作串行执行
},
// 乐观更新:立即显示完成状态
optimistic: {
enabled: true,
apply: (payload) => {
const task = store.tasks.find(t => t.id === payload.id)
const snapshot = { ...task }
task.completed = true
return snapshot
},
rollback: (snapshot) => {
Object.assign(store.tasks.find(t => t.id === snapshot.id), snapshot)
},
},
// HTTP 请求
request: {
method: 'POST',
url: (p) => `/api/tasks/${p.id}/complete`,
},
// 服务器确认后更新状态
commit: async (result, payload) => {
Object.assign(store.tasks.find(t => t.id === payload.id), result)
},
},
})3. 组件提交指令
function completeTask(id: string) {
pipeline.dispatch('task.complete', { id })
}就这样。以下全部自动处理:
- 指令生命周期追踪(无需手写 log)
- HTTP 请求发送
- 请求去重
- 乐观更新 & 失败回滚
- 资源冲突检测
- 并发控制
4. (可选)注册中断控制器
如果你需要处理 SSE、WebSocket、Web Worker 等外部事件:
pipeline.registerInterruptHandler('sse', {
onMessage: (event) => {
// 统一处理所有外部中断
pipeline.dispatch('data.sync', event.data)
},
})调度策略
不同场景需要不同的并发控制:
out-of-order(默认):最大并行
// 不同任务可以并行
pipeline.dispatch('task.fetch', { id: 1 })
pipeline.dispatch('task.fetch', { id: 2 })
pipeline.dispatch('task.fetch', { id: 3 })
// 三个请求同时发出serial:严格串行
meta: { schedulingStrategy: 'serial' }
// 同一任务的操作必须排队
pipeline.dispatch('task.update', { id: 1, name: 'A' })
pipeline.dispatch('task.update', { id: 1, name: 'B' })
// B 等待 A 完成后才执行,保证顺序latest:只保留最新(搜索场景)
meta: { schedulingStrategy: 'latest' }
// 用户快速输入
pipeline.dispatch('search', { query: 'h' })
pipeline.dispatch('search', { query: 'he' })
pipeline.dispatch('search', { query: 'hel' })
pipeline.dispatch('search', { query: 'hello' })
// 前三个自动取消,只执行 'hello'read-write:读写锁
meta: { schedulingStrategy: 'read-write' }
// 多个读操作可以并行
pipeline.dispatch('user.get', { id: 1 })
pipeline.dispatch('user.get', { id: 1 })
// 写操作独占
pipeline.dispatch('user.update', { id: 1, name: 'test' })
// 等待所有读完成,阻塞后续读,直到写完成五级流水线架构
┌─────────────────────────────────────────────────────────────────────────────┐
│ │
│ dispatch('task.complete', { id: 1 }) │
│ │ │
│ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ IF │ → │ SCH │ → │ EX │ → │ RES │ → │ WB │ │
│ │ 取指 │ │ 调度 │ │ 执行 │ │ 响应 │ │ 写回 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │ │ │
│ 生成ID 检测冲突 执行请求 处理结果 提交状态 │
│ 捕获调用源 决定并行/等待 乐观更新 错误处理 释放资源 │
│ 创建取消器 发射指令 超时控制 规范化 回滚(失败) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘安装
npm install front-cpu快速开始
import { Pipeline, setHttpClient, registerISA } from 'front-cpu'
// 1. 配置 HTTP 客户端
setHttpClient({
get: (url, config) => fetch(url, config).then(r => r.json()),
post: (url, data, config) =>
fetch(url, { method: 'POST', body: JSON.stringify(data), ...config })
.then(r => r.json()),
})
// 2. 定义指令集
registerISA({
'user.fetch': {
meta: {
description: '获取用户信息',
resourceIdentifier: (p) => [`user:${p.id}`],
},
request: {
method: 'GET',
url: (p) => `/api/users/${p.id}`,
},
commit: async (result) => {
store.user = result
},
},
})
// 3. 创建并启动流水线
const pipeline = new Pipeline({ maxConcurrency: 10 })
pipeline.start()
// 4. 提交指令
const user = await pipeline.dispatch('user.fetch', { id: 1 })启用调试(可选)
核心包不含调试代码,保持最小体积。需要调试时:
import { Pipeline, cpuConsole, ConsoleLevel } from 'front-cpu/debug'
// 设置日志级别
cpuConsole.setLevel(ConsoleLevel.VERBOSE)
// 只看特定指令
cpuConsole.setFilter(['task.update', 'task.complete'])日志级别:
SILENT- 静默MINIMAL- 只显示成功/失败NORMAL- 显示关键阶段VERBOSE- 显示所有细节 + 流水线耗时图DEBUG- 显示完整 payload 和调用栈
API 速览
Pipeline
const pipeline = new Pipeline({
maxConcurrency: 10, // 最大并发数
tickInterval: 16, // 调度器轮询间隔
})
pipeline.start() // 启动
pipeline.stop() // 暂停
pipeline.reset() // 重置
// 提交指令
const result = await pipeline.dispatch('type', payload, options)
// 取消指令
pipeline.flush('tag') // 按标签取消
pipeline.getInstructionsByTags(['tag']) // 按标签查询指令定义
registerISA({
'instruction.type': {
meta: {
description: string,
category: 'task' | 'debug' | 'schedule' | 'system' | 'area',
resourceIdentifier: (payload) => string[],
priority?: number, // 0-10
timeout?: number, // 毫秒
schedulingStrategy?: 'out-of-order' | 'serial' | 'latest' | 'read-write',
},
request?: { method, url, body }, // HTTP 请求
execute?: (payload, context) => any, // 或自定义逻辑
optimistic?: { enabled, apply, rollback },
commit?: (result, payload, context, snapshot) => void,
},
})开发
pnpm install # 安装依赖
pnpm dev # 启动 playground (localhost:3000)
pnpm build # 构建
pnpm test # 测试
pnpm bench # 性能测试文档
License
MIT
