Files
test-repo/docs/.vitepress/theme/components/appendix/concurrency-models/AsyncAwaitDemo.vue
T
sanbuphy 0eba9e87e9 fix(eslint): reduce warnings in GitHub Actions deployment
- Disable formatting rules (handled by Prettier)
- Relaxed strict Vue/JS rules for demo code compatibility
- Fix syntax errors in ApiPlayground and VoiceCloningDemo
- Fix duplicate else-if condition in ApiPlayground
- Fix Promise executor async pattern in AutoregressiveAudioDemo
- Add TypeScript file support to ESLint config

Warnings reduced from 295 to 251 problems.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-18 17:38:10 +08:00

466 lines
9.4 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.
<template>
<div class="demo-container">
<h4>async/await 机制演示</h4>
<div class="controls">
<el-button
type="primary"
size="small"
:disabled="isRunning"
@click="runExample"
>
{{ isRunning ? '运行中...' : '运行示例' }}
</el-button>
<el-button
size="small"
@click="reset"
>
重置
</el-button>
<el-checkbox
v-model="showDetails"
size="small"
>
显示详细信息
</el-checkbox>
</div>
<div class="code-section">
<div class="code-block">
<div class="code-header">
<span class="code-title">Python asyncio 示例</span>
</div>
<pre class="code-content"><code><span class="keyword">import</span> asyncio
<span class="keyword">async def</span> <span class="function">fetch_data</span>(url):
<span class="comment"># await 挂起让出 CPU</span>
response = <span class="keyword">await</span> aiohttp.get(url)
<span class="comment"># I/O 完成后继续执行</span>
<span class="keyword">return</span> response.json()
<span class="keyword">async def</span> <span class="function">main</span>():
<span class="comment"># 并发执行</span>
tasks = [fetch_data(url) <span class="keyword">for</span> url <span class="keyword">in</span> urls]
results = <span class="keyword">await</span> asyncio.gather(*tasks)</code></pre>
</div>
</div>
<div class="visualization">
<div class="timeline-container">
<h5>执行时间线</h5>
<div class="timeline">
<div class="time-axis">
<div class="axis-label">
0ms
</div>
<div class="axis-label">
50ms
</div>
<div class="axis-label">
100ms
</div>
<div class="axis-label">
150ms
</div>
<div class="axis-label">
200ms
</div>
</div>
<div class="thread-rows">
<div class="thread-row">
<div class="row-label">
事件循环
</div>
<div class="row-track">
<div
class="execution-segment event-loop"
style="width: 100%;"
>
调度中
</div>
</div>
</div>
<div
v-for="(task, idx) in tasks"
:key="task.id"
class="thread-row"
>
<div class="row-label">
任务 {{ task.id }}
</div>
<div class="row-track">
<template
v-for="(segment, sidx) in task.segments"
:key="sidx"
>
<div
class="execution-segment"
:class="{ 'active': segment.type === 'active', 'io': segment.type === 'io' }"
:style="{ left: segment.start + '%', width: segment.width + '%', backgroundColor: segment.color }"
>
<span
v-if="segment.width > 8"
class="segment-label"
>
{{ segment.type === 'active' ? '执行' : 'I/O' }}
</span>
</div>
</template>
</div>
</div>
</div>
</div>
</div>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-title">
并发任务数
</div>
<div class="stat-value">
{{ tasks.length }}
</div>
</div>
<div class="stat-card">
<div class="stat-title">
总执行时间
</div>
<div class="stat-value">
{{ totalTime }}ms
</div>
</div>
<div class="stat-card">
<div class="stat-title">
I/O 等待时间
</div>
<div class="stat-value">
{{ ioWaitTime }}ms
</div>
</div>
<div class="stat-card">
<div class="stat-title">
CPU 利用率
</div>
<div class="stat-value">
{{ cpuUtilization }}%
</div>
</div>
</div>
</div>
<div class="explanation">
<el-alert
title="async/await 的优势"
type="success"
description="当一个任务遇到 I/O 操作(如网络请求)时,await 会让出 CPU,事件循环调度其他任务执行。I/O 完成后,任务从断点恢复。这种方式让单个线程可以并发处理数千个任务。"
show-icon
:closable="false"
/>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch } from 'vue'
const comparisonMode = ref('memory')
const coroutineCount = ref(1000)
const isRunning = ref(false)
const showDetails = ref(false)
const tasks = ref([])
// 颜色
const colors = ['#409eff', '#67c23a', '#e6a23c', '#f56c6c', '#909399']
// 计算统计数据
const totalTime = computed(() => {
if (tasks.value.length === 0) return 0
// 模拟总时间
return Math.round(50 + tasks.value.length * 10)
})
const ioWaitTime = computed(() => {
return Math.round(totalTime.value * 0.6)
})
const cpuUtilization = computed(() => {
return Math.round(100 - (ioWaitTime.value / totalTime.value) * 100)
})
// 生成任务数据
function generateTasks() {
const count = Math.min(Math.floor(coroutineCount.value / 200), 5)
const newTasks = []
for (let i = 0; i < count; i++) {
const segments = []
let currentPos = 5
// 生成交替的执行和I/O段
for (let j = 0; j < 3; j++) {
// 执行段
const execWidth = 10 + Math.random() * 10
segments.push({
type: 'active',
start: currentPos,
width: execWidth,
color: colors[i % colors.length]
})
currentPos += execWidth
// I/O段
const ioWidth = 15 + Math.random() * 10
segments.push({
type: 'io',
start: currentPos,
width: ioWidth,
color: '#dcdfe6'
})
currentPos += ioWidth
}
newTasks.push({
id: i + 1,
segments,
state: 'ready'
})
}
tasks.value = newTasks
}
// 运行示例
function runExample() {
isRunning.value = true
generateTasks()
// 模拟运行
setTimeout(() => {
isRunning.value = false
}, 2000)
}
// 重置
function reset() {
tasks.value = []
isRunning.value = false
coroutineCount.value = 1000
}
// 监听协程数量变化
watch(coroutineCount, () => {
if (tasks.value.length > 0) {
generateTasks()
}
})
// 初始化
generateTasks()
</script>
<style scoped>
.demo-container {
padding: 20px;
background: #f5f7fa;
border-radius: 6px;
}
h4 {
margin: 0 0 16px 0;
color: #303133;
}
.controls {
display: flex;
gap: 16px;
align-items: center;
margin-bottom: 20px;
flex-wrap: wrap;
}
.slider-label {
font-size: 14px;
color: #606266;
min-width: 100px;
}
.code-section {
margin-bottom: 20px;
}
.code-block {
background: #282c34;
border-radius: 6px;
overflow: hidden;
}
.code-header {
background: #21252b;
padding: 8px 16px;
border-bottom: 1px solid #181a1f;
}
.code-title {
color: #abb2bf;
font-size: 13px;
font-weight: 500;
}
.code-content {
padding: 16px;
margin: 0;
overflow-x: auto;
font-family: 'Fira Code', 'Consolas', monospace;
font-size: 13px;
line-height: 1.6;
}
.keyword {
color: #c678dd;
}
.function {
color: #61afef;
}
.comment {
color: #5c6370;
font-style: italic;
}
.visualization {
margin-bottom: 20px;
}
.timeline-container {
background: white;
border-radius: 6px;
padding: 16px;
margin-bottom: 16px;
}
.timeline-container h5 {
margin: 0 0 12px 0;
color: #303133;
}
.timeline {
position: relative;
}
.time-axis {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #e4e7ed;
margin-bottom: 8px;
}
.axis-label {
font-size: 11px;
color: #909399;
}
.thread-rows {
display: flex;
flex-direction: column;
gap: 8px;
}
.thread-row {
display: grid;
grid-template-columns: 100px 1fr;
gap: 8px;
align-items: center;
}
.row-label {
font-size: 12px;
color: #606266;
text-align: right;
}
.row-track {
position: relative;
height: 24px;
background: #f5f7fa;
border-radius: 4px;
overflow: hidden;
}
.execution-segment {
position: absolute;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 10px;
color: white;
font-weight: 500;
}
.execution-segment.io {
background: #dcdfe6 !important;
color: #606266;
}
.execution-segment.event-loop {
background: #409eff;
}
.current-indicator {
position: absolute;
top: 0;
bottom: 0;
width: 2px;
background: #f56c6c;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
}
.stat-card {
background: white;
border-radius: 6px;
padding: 12px;
text-align: center;
}
.stat-title {
font-size: 12px;
color: #909399;
margin-bottom: 4px;
}
.stat-value {
font-size: 20px;
font-weight: bold;
color: #303133;
}
.explanation {
margin-top: 16px;
}
@media (max-width: 768px) {
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
.controls {
flex-direction: column;
align-items: stretch;
}
.thread-row {
grid-template-columns: 60px 1fr;
}
}
</style>