hyper-scheduler 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +21 -0
- package/.eslintrc.cjs +26 -0
- package/GEMINI.md +1 -0
- package/README.md +38 -0
- package/docs/.vitepress/config.ts +52 -0
- package/docs/README.md +120 -0
- package/docs/api/devtools.md +232 -0
- package/docs/api/index.md +178 -0
- package/docs/api/scheduler.md +322 -0
- package/docs/api/task.md +439 -0
- package/docs/api/types.md +365 -0
- package/docs/examples/index.md +295 -0
- package/docs/guide/best-practices.md +436 -0
- package/docs/guide/core-concepts.md +363 -0
- package/docs/guide/getting-started.md +138 -0
- package/docs/index.md +33 -0
- package/docs/public/logo.svg +54 -0
- package/examples/browser/index.html +354 -0
- package/examples/node/simple.js +36 -0
- package/examples/react-demo/index.html +12 -0
- package/examples/react-demo/package.json +23 -0
- package/examples/react-demo/src/App.css +212 -0
- package/examples/react-demo/src/App.jsx +160 -0
- package/examples/react-demo/src/main.jsx +9 -0
- package/examples/react-demo/vite.config.ts +12 -0
- package/examples/react-demo/yarn.lock +752 -0
- package/examples/vue-demo/index.html +12 -0
- package/examples/vue-demo/package.json +21 -0
- package/examples/vue-demo/src/App.vue +373 -0
- package/examples/vue-demo/src/main.ts +4 -0
- package/examples/vue-demo/vite.config.ts +13 -0
- package/examples/vue-demo/yarn.lock +375 -0
- package/package.json +51 -0
- package/src/constants.ts +18 -0
- package/src/core/retry-strategy.ts +28 -0
- package/src/core/scheduler.ts +601 -0
- package/src/core/task-registry.ts +58 -0
- package/src/index.ts +74 -0
- package/src/platform/browser/browser-timer.ts +66 -0
- package/src/platform/browser/main-thread-timer.ts +16 -0
- package/src/platform/browser/worker.ts +31 -0
- package/src/platform/node/debug-cli.ts +19 -0
- package/src/platform/node/node-timer.ts +15 -0
- package/src/platform/timer-strategy.ts +19 -0
- package/src/plugins/dev-tools.ts +101 -0
- package/src/types.ts +115 -0
- package/src/ui/components/devtools.ts +525 -0
- package/src/ui/components/floating-trigger.ts +102 -0
- package/src/ui/components/icons.ts +16 -0
- package/src/ui/components/resizer.ts +129 -0
- package/src/ui/components/task-detail.ts +228 -0
- package/src/ui/components/task-header.ts +319 -0
- package/src/ui/components/task-list.ts +416 -0
- package/src/ui/components/timeline.ts +364 -0
- package/src/ui/debug-panel.ts +56 -0
- package/src/ui/i18n/en.ts +76 -0
- package/src/ui/i18n/index.ts +42 -0
- package/src/ui/i18n/zh.ts +76 -0
- package/src/ui/store/dev-tools-store.ts +191 -0
- package/src/ui/styles/theme.css.ts +56 -0
- package/src/ui/styles.ts +43 -0
- package/src/utils/cron-lite.ts +221 -0
- package/src/utils/cron.ts +20 -0
- package/src/utils/id.ts +10 -0
- package/src/utils/schedule.ts +93 -0
- package/src/vite-env.d.ts +1 -0
- package/stats.html +4949 -0
- package/tests/integration/Debug.test.ts +58 -0
- package/tests/unit/Plugin.test.ts +16 -0
- package/tests/unit/RetryStrategy.test.ts +21 -0
- package/tests/unit/Scheduler.test.ts +38 -0
- package/tests/unit/schedule.test.ts +70 -0
- package/tests/unit/ui/DevToolsStore.test.ts +67 -0
- package/tsconfig.json +28 -0
- package/vite.config.ts +51 -0
- package/vitest.config.ts +24 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
# 最佳实践
|
|
2
|
+
|
|
3
|
+
本指南提供使用 Hyper Scheduler 的最佳实践和常见模式。
|
|
4
|
+
|
|
5
|
+
## 任务设计
|
|
6
|
+
|
|
7
|
+
### 保持任务简洁
|
|
8
|
+
|
|
9
|
+
任务处理函数应该专注于单一职责,避免过于复杂的逻辑。
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// ❌ 不推荐:任务过于复杂
|
|
13
|
+
scheduler.createTask({
|
|
14
|
+
id: 'complex-task',
|
|
15
|
+
schedule: '1m',
|
|
16
|
+
handler: async () => {
|
|
17
|
+
await fetchData();
|
|
18
|
+
await processData();
|
|
19
|
+
await saveToDatabase();
|
|
20
|
+
await sendNotification();
|
|
21
|
+
await cleanupOldData();
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// ✅ 推荐:拆分为多个任务
|
|
26
|
+
scheduler.createTask({
|
|
27
|
+
id: 'fetch-data',
|
|
28
|
+
schedule: '1m',
|
|
29
|
+
handler: async () => {
|
|
30
|
+
await fetchData();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
scheduler.createTask({
|
|
35
|
+
id: 'process-data',
|
|
36
|
+
schedule: '2m',
|
|
37
|
+
handler: async () => {
|
|
38
|
+
await processData();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 使用有意义的任务 ID
|
|
44
|
+
|
|
45
|
+
任务 ID 应该清晰描述任务的用途。
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// ❌ 不推荐
|
|
49
|
+
scheduler.createTask({
|
|
50
|
+
id: 'task1',
|
|
51
|
+
// ...
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// ✅ 推荐
|
|
55
|
+
scheduler.createTask({
|
|
56
|
+
id: 'daily-user-report',
|
|
57
|
+
// ...
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 合理使用标签
|
|
62
|
+
|
|
63
|
+
使用标签对任务进行分类,便于管理和过滤。
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
scheduler.createTask({
|
|
67
|
+
id: 'sync-orders',
|
|
68
|
+
schedule: '5m',
|
|
69
|
+
tags: ['sync', 'orders', 'high-priority'],
|
|
70
|
+
handler: async () => {
|
|
71
|
+
await syncOrders();
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
scheduler.createTask({
|
|
76
|
+
id: 'cleanup-logs',
|
|
77
|
+
schedule: '0 0 2 * * *',
|
|
78
|
+
tags: ['maintenance', 'low-priority'],
|
|
79
|
+
handler: async () => {
|
|
80
|
+
await cleanupLogs();
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## 错误处理
|
|
86
|
+
|
|
87
|
+
### 始终添加错误处理
|
|
88
|
+
|
|
89
|
+
为关键任务添加 `onError` 回调,确保错误被正确处理。
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
scheduler.createTask({
|
|
93
|
+
id: 'critical-sync',
|
|
94
|
+
schedule: '1m',
|
|
95
|
+
handler: async () => {
|
|
96
|
+
await criticalOperation();
|
|
97
|
+
},
|
|
98
|
+
options: {
|
|
99
|
+
onError: (error, taskId) => {
|
|
100
|
+
// 记录错误
|
|
101
|
+
logger.error(`Task ${taskId} failed:`, error);
|
|
102
|
+
|
|
103
|
+
// 发送告警
|
|
104
|
+
alertService.send({
|
|
105
|
+
title: 'Critical Task Failed',
|
|
106
|
+
message: error.message,
|
|
107
|
+
taskId
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 配置重试策略
|
|
115
|
+
|
|
116
|
+
对于可能因网络等临时问题失败的任务,配置合理的重试策略。
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
scheduler.createTask({
|
|
120
|
+
id: 'api-call',
|
|
121
|
+
schedule: '5m',
|
|
122
|
+
handler: async () => {
|
|
123
|
+
await callExternalAPI();
|
|
124
|
+
},
|
|
125
|
+
options: {
|
|
126
|
+
retry: {
|
|
127
|
+
maxAttempts: 3,
|
|
128
|
+
initialDelay: 1000,
|
|
129
|
+
factor: 2 // 指数退避:1s, 2s, 4s
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## 调度规则
|
|
136
|
+
|
|
137
|
+
### 选择合适的调度方式
|
|
138
|
+
|
|
139
|
+
根据需求选择 Cron 表达式或时间间隔。
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// 固定时间点执行 - 使用 Cron
|
|
143
|
+
scheduler.createTask({
|
|
144
|
+
id: 'daily-report',
|
|
145
|
+
schedule: '0 0 9 * * *', // 每天 9:00
|
|
146
|
+
handler: async () => {
|
|
147
|
+
await generateDailyReport();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// 固定间隔执行 - 使用时间间隔
|
|
152
|
+
scheduler.createTask({
|
|
153
|
+
id: 'health-check',
|
|
154
|
+
schedule: '30s', // 每 30 秒
|
|
155
|
+
handler: async () => {
|
|
156
|
+
await checkHealth();
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### 避免过于频繁的任务
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// ❌ 不推荐:过于频繁
|
|
165
|
+
scheduler.createTask({
|
|
166
|
+
id: 'check',
|
|
167
|
+
schedule: '*/1 * * * * *', // 每秒执行
|
|
168
|
+
handler: async () => {
|
|
169
|
+
await heavyOperation();
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// ✅ 推荐:合理的频率
|
|
174
|
+
scheduler.createTask({
|
|
175
|
+
id: 'check',
|
|
176
|
+
schedule: '30s', // 每 30 秒
|
|
177
|
+
handler: async () => {
|
|
178
|
+
await heavyOperation();
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## 时区处理
|
|
184
|
+
|
|
185
|
+
### 全局时区配置
|
|
186
|
+
|
|
187
|
+
为整个调度器设置统一时区。
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
const scheduler = new Scheduler({
|
|
191
|
+
timezone: 'Asia/Shanghai'
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### 任务级时区覆盖
|
|
196
|
+
|
|
197
|
+
为特定任务设置不同时区。
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
// 东京时间每天 9:00
|
|
201
|
+
scheduler.createTask({
|
|
202
|
+
id: 'tokyo-report',
|
|
203
|
+
schedule: '0 0 9 * * *',
|
|
204
|
+
handler: () => generateReport('Tokyo'),
|
|
205
|
+
options: {
|
|
206
|
+
timezone: 'Asia/Tokyo'
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// 纽约时间每天 9:00
|
|
211
|
+
scheduler.createTask({
|
|
212
|
+
id: 'ny-report',
|
|
213
|
+
schedule: '0 0 9 * * *',
|
|
214
|
+
handler: () => generateReport('New York'),
|
|
215
|
+
options: {
|
|
216
|
+
timezone: 'America/New_York'
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## 性能优化
|
|
222
|
+
|
|
223
|
+
### 控制历史记录数量
|
|
224
|
+
|
|
225
|
+
根据需求调整历史记录保留数量。
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// 生产环境:减少内存占用
|
|
229
|
+
const scheduler = new Scheduler({
|
|
230
|
+
maxHistory: 20
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// 开发环境:保留更多历史用于调试
|
|
234
|
+
const scheduler = new Scheduler({
|
|
235
|
+
maxHistory: 100
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### 避免任务重叠
|
|
240
|
+
|
|
241
|
+
确保任务执行时间小于调度间隔。
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// ❌ 不推荐:任务可能重叠
|
|
245
|
+
scheduler.createTask({
|
|
246
|
+
id: 'slow-task',
|
|
247
|
+
schedule: '10s', // 每 10 秒
|
|
248
|
+
handler: async () => {
|
|
249
|
+
await slowOperation(); // 可能需要 15 秒
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// ✅ 推荐:增加间隔或优化任务
|
|
254
|
+
scheduler.createTask({
|
|
255
|
+
id: 'slow-task',
|
|
256
|
+
schedule: '30s', // 增加到 30 秒
|
|
257
|
+
handler: async () => {
|
|
258
|
+
await optimizedOperation(); // 优化后 5 秒完成
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## 调试与监控
|
|
264
|
+
|
|
265
|
+
### 开发环境启用调试
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
269
|
+
|
|
270
|
+
const scheduler = new Scheduler({
|
|
271
|
+
debug: isDev,
|
|
272
|
+
maxHistory: isDev ? 100 : 20
|
|
273
|
+
});
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### 浏览器环境使用 DevTools
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
import { Scheduler, DevTools } from 'hyper-scheduler';
|
|
280
|
+
|
|
281
|
+
const scheduler = new Scheduler({
|
|
282
|
+
debug: true,
|
|
283
|
+
plugins: [
|
|
284
|
+
new DevTools({
|
|
285
|
+
theme: 'auto',
|
|
286
|
+
language: 'zh'
|
|
287
|
+
})
|
|
288
|
+
]
|
|
289
|
+
});
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### 监听关键事件
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
// 监控任务失败
|
|
296
|
+
scheduler.on('task_failed', ({ taskId, error }) => {
|
|
297
|
+
metrics.increment('task_failed', { taskId });
|
|
298
|
+
logger.error(`Task ${taskId} failed:`, error);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// 监控任务执行时间
|
|
302
|
+
scheduler.on('task_completed', ({ taskId, duration }) => {
|
|
303
|
+
metrics.timing('task_duration', duration, { taskId });
|
|
304
|
+
|
|
305
|
+
if (duration > 5000) {
|
|
306
|
+
logger.warn(`Task ${taskId} took ${duration}ms`);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## 生命周期管理
|
|
312
|
+
|
|
313
|
+
### 优雅关闭
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
// Node.js 环境
|
|
317
|
+
process.on('SIGTERM', () => {
|
|
318
|
+
scheduler.stop();
|
|
319
|
+
process.exit(0);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
// 浏览器环境
|
|
323
|
+
window.addEventListener('beforeunload', () => {
|
|
324
|
+
scheduler.stop();
|
|
325
|
+
});
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### 动态任务管理
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
// 根据配置动态创建任务
|
|
332
|
+
function setupTasks(config) {
|
|
333
|
+
config.tasks.forEach(taskConfig => {
|
|
334
|
+
scheduler.createTask({
|
|
335
|
+
id: taskConfig.id,
|
|
336
|
+
schedule: taskConfig.schedule,
|
|
337
|
+
handler: taskConfig.handler,
|
|
338
|
+
tags: taskConfig.tags
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// 运行时更新任务
|
|
344
|
+
function updateTask(taskId, newSchedule) {
|
|
345
|
+
scheduler.deleteTask(taskId);
|
|
346
|
+
scheduler.createTask({
|
|
347
|
+
id: taskId,
|
|
348
|
+
schedule: newSchedule,
|
|
349
|
+
handler: originalHandler
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## 常见模式
|
|
355
|
+
|
|
356
|
+
### 数据同步
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
scheduler.createTask({
|
|
360
|
+
id: 'sync-users',
|
|
361
|
+
schedule: '5m',
|
|
362
|
+
tags: ['sync', 'users'],
|
|
363
|
+
handler: async () => {
|
|
364
|
+
const users = await fetchRemoteUsers();
|
|
365
|
+
await saveToLocal(users);
|
|
366
|
+
},
|
|
367
|
+
options: {
|
|
368
|
+
retry: {
|
|
369
|
+
maxAttempts: 3,
|
|
370
|
+
initialDelay: 2000
|
|
371
|
+
},
|
|
372
|
+
onError: (error, taskId) => {
|
|
373
|
+
logger.error(`Sync failed: ${error.message}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### 定时报告
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
scheduler.createTask({
|
|
383
|
+
id: 'daily-report',
|
|
384
|
+
schedule: '0 0 9 * * *', // 每天 9:00
|
|
385
|
+
tags: ['report', 'daily'],
|
|
386
|
+
handler: async () => {
|
|
387
|
+
const report = await generateReport();
|
|
388
|
+
await sendEmail(report);
|
|
389
|
+
},
|
|
390
|
+
options: {
|
|
391
|
+
timezone: 'Asia/Shanghai',
|
|
392
|
+
onError: (error, taskId) => {
|
|
393
|
+
alertService.send({
|
|
394
|
+
title: 'Report Generation Failed',
|
|
395
|
+
message: error.message
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### 健康检查
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
scheduler.createTask({
|
|
406
|
+
id: 'health-check',
|
|
407
|
+
schedule: '30s',
|
|
408
|
+
tags: ['monitoring', 'health'],
|
|
409
|
+
handler: async () => {
|
|
410
|
+
const isHealthy = await checkSystemHealth();
|
|
411
|
+
if (!isHealthy) {
|
|
412
|
+
await sendAlert('System unhealthy');
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### 数据清理
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
scheduler.createTask({
|
|
422
|
+
id: 'cleanup-old-logs',
|
|
423
|
+
schedule: '0 0 2 * * *', // 每天凌晨 2:00
|
|
424
|
+
tags: ['maintenance', 'cleanup'],
|
|
425
|
+
handler: async () => {
|
|
426
|
+
const cutoffDate = Date.now() - 30 * 24 * 60 * 60 * 1000; // 30 天前
|
|
427
|
+
await deleteLogsOlderThan(cutoffDate);
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
## 相关链接
|
|
433
|
+
|
|
434
|
+
- [核心概念](./core-concepts.md) - 深入理解调度机制
|
|
435
|
+
- [Scheduler API](../api/scheduler.md) - 完整 API 参考
|
|
436
|
+
- [Task API](../api/task.md) - 任务配置详解
|