Files
test-repo/docs/.vitepress/theme/components/appendix/async-task-queues/TaskRetryDemo.vue
T
sanbuphy df51f84ab5 docs: 重构 README 附录展示 & 新增多个附录交互组件
README 更新:
- 移除顶部 header.png 横幅图片
- 新增「附录知识库」板块,以 3×3 网格展示 9 大知识领域精选内容
- 附录链接指向部署版网站 (datawhalechina.github.io)
- 阶段表格新增「附录」行,突出 80+ 交互式专题
- 章节标题「新手入门 & PM」简化为「零基础入门」
- News 新增 2026-02-25 附录知识库更新条目

新增交互组件:
- 异步任务队列 (async-task-queues) 演示组件
- 文件存储 (file-storage) 演示组件
- 项目架构 (project-architecture) 演示组件
- 限流与背压 (rate-limiting) 演示组件
- 搜索引擎 (search-engines) 演示组件
- 计算机基础: AppLaunch/BiosUefi/OSBoot 等启动流程演示组件

新增附录文档:
- 前端项目架构 (frontend-project-architecture.md)
- 后端项目架构 (backend-project-architecture.md)

内容优化:
- 算法思维、数据结构、编程语言、调试艺术等多篇附录内容更新
- HTML/CSS 布局、请求旅程等前后端文档完善
- 附录索引页 (index.md) 同步更新
2026-02-25 12:22:49 +08:00

169 lines
6.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
TaskRetryDemo.vue
任务重试机制演示展示失败重试和退避策略
-->
<template>
<div class="retry-demo">
<div class="header">
<div class="title">任务重试与退避策略</div>
<div class="subtitle">模拟任务失败后的重试过程</div>
</div>
<div class="strategy-tabs">
<button
v-for="s in strategies"
:key="s.key"
:class="['tab', { active: strategy === s.key }]"
@click="strategy = s.key; reset()"
>{{ s.label }}</button>
</div>
<div class="retry-area">
<button class="start-btn" @click="startRetry" :disabled="running">
{{ running ? '重试中...' : '执行任务(模拟失败)' }}
</button>
<div class="attempts">
<div
v-for="(attempt, i) in attempts"
:key="i"
:class="['attempt', attempt.status]"
>
<div class="attempt-header">
<span class="attempt-num"> {{ i + 1 }} {{ i === 0 ? '执行' : '重试' }}</span>
<span :class="['status-badge', attempt.status]">
{{ attempt.status === 'success' ? '成功' : attempt.status === 'fail' ? '失败' : attempt.status === 'waiting' ? '等待中' : '执行中' }}
</span>
</div>
<div class="attempt-detail">
<span v-if="attempt.delay > 0">等待 {{ attempt.delay }}s 后重试</span>
<span v-if="attempt.error" class="error-msg">{{ attempt.error }}</span>
</div>
</div>
</div>
</div>
<div class="strategy-info">
<div class="info-title">{{ currentStrategy.label }}</div>
<div class="info-desc">{{ currentStrategy.desc }}</div>
<div class="info-formula">
延迟公式<code>{{ currentStrategy.formula }}</code>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const strategy = ref('fixed')
const running = ref(false)
const attempts = ref([])
const strategies = [
{ key: 'fixed', label: '固定间隔', desc: '每次重试等待相同的时间,简单但可能造成"重试风暴"', formula: 'delay = 2s' },
{ key: 'exponential', label: '指数退避', desc: '每次重试等待时间翻倍,有效避免服务端过载', formula: 'delay = 2^n 秒 (1s, 2s, 4s, 8s...)' },
{ key: 'jitter', label: '指数退避+抖动', desc: '在指数退避基础上加随机偏移,防止多个客户端同时重试', formula: 'delay = 2^n + random(0, 1s)' }
]
const currentStrategy = computed(() => strategies.find(s => s.key === strategy.value))
function reset() {
running.value = false
attempts.value = []
}
function getDelay(n) {
if (strategy.value === 'fixed') return 2
if (strategy.value === 'exponential') return Math.pow(2, n)
return Math.pow(2, n) + Math.random().toFixed(1) * 1
}
async function sleep(ms) {
return new Promise(r => setTimeout(r, ms))
}
async function startRetry() {
reset()
running.value = true
const maxRetries = 4
const failUntil = 2 + Math.floor(Math.random() * 2) // succeed on 3rd or 4th attempt
for (let i = 0; i <= maxRetries; i++) {
const delay = i === 0 ? 0 : getDelay(i - 1)
const attempt = { status: 'waiting', delay, error: '' }
attempts.value.push(attempt)
if (delay > 0) {
await sleep(Math.min(delay * 500, 2000))
}
attempt.status = 'running'
await sleep(500)
if (i < failUntil) {
attempt.status = 'fail'
attempt.error = ['连接超时', '服务不可用', '网络错误'][i % 3]
} else {
attempt.status = 'success'
running.value = false
return
}
}
running.value = false
}
</script>
<style scoped>
.retry-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.header { margin-bottom: 1rem; }
.title { font-weight: 700; font-size: 1.1rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.strategy-tabs { display: flex; gap: 0.5rem; margin-bottom: 1rem; }
.tab {
padding: 0.4rem 0.8rem; border-radius: 6px; border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg); cursor: pointer; font-size: 0.85rem;
}
.tab.active { border-color: var(--vp-c-brand); color: var(--vp-c-brand); }
.start-btn {
padding: 0.5rem 1.5rem; border-radius: 6px; border: none;
background: var(--vp-c-brand); color: #fff; cursor: pointer; font-size: 0.9rem;
margin-bottom: 1rem;
}
.start-btn:disabled { opacity: 0.6; cursor: not-allowed; }
.attempts { display: flex; flex-direction: column; gap: 0.5rem; }
.attempt {
padding: 0.6rem 0.75rem; border-radius: 8px; background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
}
.attempt.fail { border-color: rgba(239,68,68,0.4); }
.attempt.success { border-color: #22c55e; background: rgba(34,197,94,0.05); }
.attempt.running { border-color: var(--vp-c-brand); }
.attempt-header { display: flex; justify-content: space-between; align-items: center; }
.attempt-num { font-weight: 600; font-size: 0.85rem; }
.status-badge { font-size: 0.75rem; padding: 0.15rem 0.5rem; border-radius: 4px; }
.status-badge.fail { background: rgba(239,68,68,0.1); color: #ef4444; }
.status-badge.success { background: rgba(34,197,94,0.1); color: #22c55e; }
.status-badge.running { background: rgba(var(--vp-c-brand-rgb),0.1); color: var(--vp-c-brand); }
.status-badge.waiting { background: var(--vp-c-bg-soft); color: var(--vp-c-text-3); }
.attempt-detail { font-size: 0.8rem; color: var(--vp-c-text-2); margin-top: 0.25rem; }
.error-msg { color: #ef4444; margin-left: 0.5rem; }
.strategy-info {
margin-top: 1rem; padding: 0.75rem; border-radius: 8px;
background: rgba(var(--vp-c-brand-rgb),0.05); border: 1px solid var(--vp-c-brand);
}
.info-title { font-weight: 700; font-size: 0.9rem; margin-bottom: 0.25rem; }
.info-desc { font-size: 0.85rem; color: var(--vp-c-text-2); margin-bottom: 0.5rem; }
.info-formula { font-size: 0.85rem; }
.info-formula code {
padding: 0.15rem 0.4rem; background: var(--vp-c-bg); border-radius: 4px;
font-family: var(--vp-font-family-mono); font-size: 0.8rem;
}
</style>