workerman==助你理解顺序执行和并行执行

贵哥的编程之路

安装这两个才能协程composer require workerman/workerman ^5.0
composer require revolt/event-loop

server.php

php server.php start第一步

<?php
require_once __DIR__ . '/vendor/autoload.php';

use Workerman\Worker;
use Workerman\Timer;
use Workerman\Connection\TcpConnection;
use Workerman\Protocols\Http\Request;
use Workerman\Protocols\Http\Response;
use Revolt\EventLoop;

// 创建 HTTP Worker
$worker = new Worker('http://0.0.0.0:8080');
$worker->count = 4;
$worker->name = 'CoroutineDemo';

$worker->onWorkerStart = function($worker) {
    echo "Workerman 协程服务器启动成功!\n";
    echo "访问: http://127.0.0.1:8080\n";
};

$worker->onMessage = function(TcpConnection $connection, Request $request) {
    $path = $request->path();

    switch ($path) {
        case '/':
            $connection->send(createResponse(
                "<h1>Workerman 5.x 协程示例</h1>
                <ul>
                    <li><a href='/async-tasks'>多个异步任务并发</a></li>
                    <li><a href='/sequential'>顺序执行异步任务</a></li>
                    <li><a href='/timer'>定时器协程</a></li>
                    <li><a href='/mock-db'>模拟数据库查询</a></li>
                </ul>"
            ));
            break;

        case '/async-tasks':
            handleAsyncTasks($connection);
            break;

        case '/sequential':
            handleSequential($connection);
            break;

        case '/timer':
            handleTimer($connection);
            break;

        case '/mock-db':
            handleMockDatabase($connection);
            break;

        default:
            // 处理静态文件
            $filePath = __DIR__ . $path;

            // 如果请求根目录,尝试加载 index.html
            if ($path === '/') {
                $filePath = __DIR__ . '/index.html';
            }

            // 调试日志
            echo "请求路径: $path\n";
            echo "文件路径: $filePath\n";
            echo "文件存在: " . (file_exists($filePath) ? '是' : '否') . "\n";
            echo "是文件: " . (is_file($filePath) ? '是' : '否') . "\n\n";

            // 检查文件是否存在
            if (file_exists($filePath) && is_file($filePath)) {
                $extension = pathinfo($filePath, PATHINFO_EXTENSION);
                $mimeTypes = [
                    'html' => 'text/html',
                    'css' => 'text/css',
                    'js' => 'application/javascript',
                    'json' => 'application/json',
                    'png' => 'image/png',
                    'jpg' => 'image/jpeg',
                    'jpeg' => 'image/jpeg',
                    'gif' => 'image/gif',
                    'svg' => 'image/svg+xml',
                ];

                $contentType = $mimeTypes[$extension] ?? 'application/octet-stream';

                $connection->send(new Response(200, [
                    'Content-Type' => $contentType . '; charset=utf-8',
                    'Server' => 'Workerman/5.0'
                ], file_get_contents($filePath)));
            } else {
                $connection->send(createResponse('<h1>404 Not Found</h1>', 404));
            }
    }
};

// 示例1: 并发执行多个异步任务
function handleAsyncTasks(TcpConnection $connection) {
    $startTime = microtime(true);

    // 使用 EventLoop 的协程特性
    $results = [];
    $completed = 0;
    $total = 3;

    // 异步任务1: 模拟 HTTP 请求
    EventLoop::delay(1.0, function() use (&$results, &$completed, $total, $connection, $startTime) {
        $results['http'] = '模拟 HTTP 请求完成';
        $completed++;
        checkAndSend($connection, $results, $completed, $total, $startTime);
    });

    // 异步任务2: 模拟数据库查询
    EventLoop::delay(1.5, function() use (&$results, &$completed, $total, $connection, $startTime) {
        $results['database'] = '模拟数据库查询完成';
        $completed++;
        checkAndSend($connection, $results, $completed, $total, $startTime);
    });

    // 异步任务3: 模拟 Redis 操作
    EventLoop::delay(0.8, function() use (&$results, &$completed, $total, $connection, $startTime) {
        $results['redis'] = '模拟 Redis 操作完成';
        $completed++;
        checkAndSend($connection, $results, $completed, $total, $startTime);
    });
}

function checkAndSend($connection, $results, $completed, $total, $startTime) {
    if ($completed === $total) {
        $duration = round((microtime(true) - $startTime) * 1000, 2);
        $html = "<h1>并发异步任务示例</h1>";
        $html .= "<p><strong>执行时间: {$duration}ms</strong></p>";
        $html .= "<h3>结果:</h3><ul>";
        foreach ($results as $key => $value) {
            $html .= "<li><strong>{$key}:</strong> {$value}</li>";
        }
        $html .= "</ul>";
        $html .= "<p><a href='/'>返回首页</a></p>";
        $connection->send(createResponse($html));
    }
}

// 示例2: 顺序执行异步任务
function handleSequential(TcpConnection $connection) {
    $startTime = microtime(true);

    // 第一步
    EventLoop::delay(0.5, function() use ($connection, $startTime) {
        $step1 = '第一步: 验证用户';

        // 第二步
        EventLoop::delay(0.5, function() use ($connection, $startTime, $step1) {
            $step2 = '第二步: 查询数据';

            // 第三步
            EventLoop::delay(0.5, function() use ($connection, $startTime, $step1, $step2) {
                $step3 = '第三步: 返回结果';

                $duration = round((microtime(true) - $startTime) * 1000, 2);
                $html = "<h1>顺序执行异步任务</h1>";
                $html .= "<p><strong>总执行时间: {$duration}ms</strong></p>";
                $html .= "<ol>";
                $html .= "<li>{$step1} ✓</li>";
                $html .= "<li>{$step2} ✓</li>";
                $html .= "<li>{$step3} ✓</li>";
                $html .= "</ol>";
                $html .= "<p><a href='/'>返回首页</a></p>";
                $connection->send(createResponse($html));
            });
        });
    });
}

// 示例3: 定时器协程
function handleTimer(TcpConnection $connection) {
    $count = 0;
    $maxCount = 5;
    $results = [];

    $timerId = Timer::add(0.3, function() use (&$count, $maxCount, &$results, &$timerId, $connection) {
        $count++;
        $results[] = "定时任务执行第 {$count} 次 - " . date('H:i:s');

        if ($count >= $maxCount) {
            Timer::del($timerId);

            $html = "<h1>定时器协程示例</h1>";
            $html .= "<p>每 300ms 执行一次,共执行 {$maxCount} 次</p>";
            $html .= "<h3>执行记录:</h3><ol>";
            foreach ($results as $result) {
                $html .= "<li>{$result}</li>";
            }
            $html .= "</ol>";
            $html .= "<p><a href='/'>返回首页</a></p>";
            $connection->send(createResponse($html));
        }
    });
}

// 示例4: 模拟数据库查询
function handleMockDatabase(TcpConnection $connection) {
    $startTime = microtime(true);

    // 模拟复杂的数据库查询流程
    // 1. 查询用户信息
    EventLoop::delay(0.3, function() use ($connection, $startTime) {
        $user = [
            'id' => 1,
            'name' => '张三',
            'email' => 'zhangsan@example.com'
        ];

        // 2. 根据用户ID查询订单
        EventLoop::delay(0.4, function() use ($connection, $startTime, $user) {
            $orders = [
                ['id' => 101, 'product' => '商品A', 'price' => 99.00],
                ['id' => 102, 'product' => '商品B', 'price' => 199.00],
            ];

            // 3. 查询用户积分
            EventLoop::delay(0.2, function() use ($connection, $startTime, $user, $orders) {
                $points = 1580;

                $duration = round((microtime(true) - $startTime) * 1000, 2);
                $html = "<h1>模拟数据库查询</h1>";
                $html .= "<p><strong>总执行时间: {$duration}ms</strong></p>";

                $html .= "<h3>用户信息:</h3>";
                $html .= "<ul>";
                $html .= "<li>ID: {$user['id']}</li>";
                $html .= "<li>姓名: {$user['name']}</li>";
                $html .= "<li>邮箱: {$user['email']}</li>";
                $html .= "</ul>";

                $html .= "<h3>订单列表:</h3><ul>";
                foreach ($orders as $order) {
                    $html .= "<li>订单#{$order['id']}: {$order['product']} - ¥{$order['price']}</li>";
                }
                $html .= "</ul>";

                $html .= "<h3>用户积分:</h3>";
                $html .= "<p>{$points} 积分</p>";

                $html .= "<p><a href='/'>返回首页</a></p>";
                $connection->send(createResponse($html));
            });
        });
    });
}

// 创建 HTTP 响应
function createResponse(string $body, int $status = 200): Response {
    return new Response($status, [
        'Content-Type' => 'text/html; charset=utf-8',
        'Server' => 'Workerman/5.0'
    ], "<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8'>
    <title>Workerman 协程示例</title>
    <style>
        body { font-family: Arial, sans-serif; max-width: 800px; margin: 50px auto; padding: 20px; }
        h1 { color: #333; }
        a { color: #0066cc; text-decoration: none; }
        a:hover { text-decoration: underline; }
        ul, ol { line-height: 1.8; }
        code { background: #f4f4f4; padding: 2px 6px; border-radius: 3px; }
    </style>
</head>
<body>
    {$body}
</body>
</html>");
}

// 运行所有 Worker
Worker::runAll();

解释

我用超级通俗的方式给你讲解整个 server.php 文件!

              依赖和初始化 (第1-14行)

<?php
require_once __DIR__ . '/vendor/autoload.php'; // 加载Composer安装的库

use Workerman\Worker; // 引入Workerman核心类
use Workerman\Timer; // 引入定时器功能
use Workerman\Connection\TcpConnection; // 引入连接管理
use Workerman\Protocols\Http\Request; // 引入HTTP请求处理
use Workerman\Protocols\Http\Response; // 引入HTTP响应处理
use Revolt\EventLoop; // 引入事件循环(协程的核心)

// 创建 HTTP Worker
$worker = new Worker('http://0.0.0.0:8080'); // 创建HTTP服务器,监听8080端口
$worker->count = 4; // 启动4个进程(利用多核CPU)
$worker->name = 'CoroutineDemo'; // 给服务器取个名字

          餐厅,先准备好各种工具(引入库)
                    创建服务器)
                    )来服务客人

                启动回调 (第16-19行)

$worker->onWorkerStart = function($worker) {
echo "Workerman 协程服务器启动成功!\n";
echo "访问: http://127.0.0.1:8080\n";

                喊一声"开始营业啦!"

                      21-93行)

          ,决定不同的URL请求该怎么处理!

$worker->onMessage = function(TcpConnection $connection, Request $request) {
$path = $request->path(); // 获取访问的路径,比如 /async-tasks

  switch ($path) {
      case '/':  // 访问首页
          // 返回一个简单的导航页面
          break;

      case '/async-tasks':  // 访问 /async-tasks
          handleAsyncTasks($connection);  用并发执行函数
          break;

      case '/sequential':  // 访问 /sequential
          handleSequential($connection);  // 调用顺序执行函数
          break;

      case '/timer':
          handleTimer($connection);  // 调用定时器函数
          break;

      case '/mock-db':
          handleMockDatabase($connection);  // 调用数据库查询函数
          break;

      default:  // 其他路径(处理静态文件)
          // 第53-91行:处理HTML、CSS、JS等静态文件
          // 如果文件存在就返回,不存在就返回404
  }

            单,客人点什么菜,就做什么菜
             asks → 做"并发执行"这道菜
              al → 做"顺序执行"这道菜
          x.html → 给你看菜单(静态文件)

                行示例 (第95-124行)

function handleAsyncTasks(TcpConnection $connection) {
$startTime = microtime(true); // 记录开始时间

  $results = [];      // 存储结果
  $completed = 0;     // 已完成任务数
  $total = 3;         // 总任务数

  // 🌐 任务1:模拟HTTP请求(1秒后完成)
  EventLoop::delay(1.0, function() use (&$results, &$completed, ...) {
      $results['http'] = '模拟 HTTP 请求完成';
      $completed++;
      checkAndSend(...);  // 检查是否全部完成
  });

  // 💾 任务2:模拟数据库查询(1.5秒后完成)
  EventLoop::delay(1.5, function() use (&$results, &$completed, ...) {
      $results['database'] = '模拟数据库查询完成';
      $completed++;
      checkAndSend(...);
  });

  // ⚡ 任务3:模拟Redis操作(0.8秒后完成)
  EventLoop::delay(0.8, function() use (&$results, &$completed, ...) {
      $results['redis'] = '模拟 Redis 操作完成';
      $completed++;
      checkAndSend(...);
  });

传统方式(顺序执行):
窗口1:👨 办业务1秒 → 👩 办业务1.5秒 → 👴 办业务0.8秒
总时间 = 1 + 1.5 + 0.8 = 3.3秒

协程方式(并发执行):
窗口1:👨 办业务(1秒) }
窗口2:👩 办业务(1.5秒) } 三个人同时开始!
窗口3:👴 办业务(0.8秒) }

           delay(1.0, function() {...})
                        数"
                  续执行下一行代码
                      非阻塞!

              并发送结果 (第126-139行)

function checkAndSend($connection, $results, $completed, $total, $startTime) {
if ($completed === $total) { // 如果3个任务都完成了
$duration = round((microtime(true) - $startTime) * 1000, 2);
// 计算总耗时(毫秒)

      // 生成HTML响应
      $html = "<h1>并发异步任务示例</h1>";
      $html .= "<p><strong>执行时间: {$duration}ms</strong></p>";
      // ... 显示所有结果

      $connection->send(createResponse($html));  // 返回给浏览器
  }
  • 就像银行柜台,等3个窗口都办完了,统一告诉客户"全部办完了,用了XX时间"

                行示例 (第141-170行)

    function handleSequential(TcpConnection $connection) {
    $startTime = microtime(true);

    // 第一步(0.5秒后执行)
    EventLoop::delay(0.5, function() use (...) {
    $step1 = '第一步: 验证用户';

      // 第二步(再过0.5秒执行,嵌套在第一步里面)
      EventLoop::delay(0.5, function() use (...) {
          $step2 = '第二步: 查询数据';
    
          // 第三步(再过0.5秒执行,嵌套在第二步里面)
          EventLoop::delay(0.5, function() use (...) {
              $step3 = '第三步: 返回结果';
              // 全部完成,返回结果
          });
      });

    });

    像排队办事,必须等前一个人办完,下一个人才能开始
    执行

    • 0.5 = 1.5秒

                例 (第172-196行)

    function handleTimer(TcpConnection $connection) {
    $count = 0;
    $maxCount = 5; // 执行5次
    $results = [];

    // 创建定时器,每300毫秒执行一次
    $timerId = Timer::add(0.3, function() use (&$count, ...) {
    $count++;
    $results[] = "定时任务执行第 {$count} 次 - " . date('H:i:s');

      if ($count >= $maxCount) {
          Timer::del($timerId);  // 删除定时器
          // 返回结果
      }

    });

                       响一次
    
              数据库查询 (第198-247行)

    function handleMockDatabase(TcpConnection $connection) {
    $startTime = microtime(true);

    // 1. 先查用户信息(0.3秒)
    EventLoop::delay(0.3, function() use (...) {
    $user = ['id' => 1, 'name' => '张三', ...];

      // 2. 再查订单(0.4秒)
      EventLoop::delay(0.4, function() use (...) {
          $orders = [['id' => 101, ...], ...];
    
          // 3. 最后查积分(0.2秒)
          EventLoop::delay(0.2, function() use (...) {
              $points = 1580;
              // 返回所有结果
              // 总时间 = 0.3 + 0.4 + 0.2 = 0.9秒
          });
      });

    });

      实场景:先查用户 → 再查订单 → 最后查积分
  • 三个查询依次进行(因为订单需要用户ID,积分需要订单信息)

               TTP响应 (第249-272行)

    function createResponse(string $body, int $status = 200): Response {
    return new Response($status, [
    'Content-Type' => 'text/html; charset=utf-8',
    'Server' => 'Workerman/5.0'
    ], "<!DOCTYPE html>
    <html>
    <head>
    <meta charset='utf-8'>
    <title>Workerman 协程示例</title>
    <style>...</style>
    </head>
    <body>
    {$body} <!-- 这里插入动态内容 -->
    </body>
    </html>");

  • 就像给菜品装盘,把结果包装成漂亮的HTML页面返回给浏览器

                 务器 (第274-275行)

    r::runAll(); // 启动所有Worker,开始监听请求

                    ,开门营业!
              循环,不断监听客户端请求

    传统PHP(同步阻塞):
    任务1 → 等待 → 任务2 → 等待 → 任务3
    执行时间 = 累加

    协程(异步非阻塞):
    任务1 ↘
    任务2 → 同时执行
    任务3 ↗

    EventLoop::delay(1.0, function() {
    echo "1秒后执行";
    });
    echo "立即执行,不等待!"; // 先输出这个

    // 输出顺序:
    // 1. "立即执行,不等待!"

                时调用多个第三方接口
                         表
                         件
                         息
    场景 顺序执行 并发执行 提升
    3个任务(1s+1.5s+0.8s) 3.3秒 1.5秒 54%
    10个网络请求(各1s) 10秒 1秒 90%

easy_demo.html

然后http://127.0.0.1:8080/easy-demo.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>协程到底有多快?一看就懂!</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Microsoft YaHei', Arial, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
        }

        .header {
            text-align: center;
            color: white;
            margin-bottom: 40px;
        }

        h1 {
            font-size: 3em;
            margin-bottom: 20px;
            text-shadow: 3px 3px 6px rgba(0,0,0,0.3);
        }

        .subtitle {
            font-size: 1.5em;
            margin-bottom: 15px;
        }

        .highlight {
            background: rgba(255,215,0,0.3);
            padding: 5px 15px;
            border-radius: 10px;
            color: #ffd700;
            font-weight: bold;
        }

        /* 比喻说明卡片 */
        .explanation-card {
            background: white;
            border-radius: 20px;
            padding: 40px;
            margin-bottom: 30px;
            box-shadow: 0 15px 50px rgba(0,0,0,0.3);
        }

        .explanation-title {
            font-size: 2em;
            color: #667eea;
            margin-bottom: 25px;
            text-align: center;
        }

        .comparison-row {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 30px;
            margin-bottom: 30px;
        }

        .comparison-box {
            padding: 25px;
            border-radius: 15px;
            text-align: center;
        }

        .box-sequential {
            background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
            color: white;
        }

        .box-concurrent {
            background: linear-gradient(135deg, #51cf66 0%, #37b24d 100%);
            color: white;
        }

        .comparison-box h3 {
            font-size: 1.8em;
            margin-bottom: 15px;
        }

        .comparison-box p {
            font-size: 1.2em;
            line-height: 1.8;
            margin-bottom: 15px;
        }

        .time-display {
            font-size: 3em;
            font-weight: bold;
            margin: 20px 0;
            padding: 20px;
            background: rgba(255,255,255,0.2);
            border-radius: 15px;
        }

        .emoji {
            font-size: 3em;
            margin-bottom: 15px;
            display: block;
        }

        /* 实际演示区域 */
        .demo-section {
            background: white;
            border-radius: 20px;
            padding: 40px;
            margin-bottom: 30px;
            box-shadow: 0 15px 50px rgba(0,0,0,0.3);
        }

        .demo-title {
            font-size: 2.2em;
            color: #667eea;
            margin-bottom: 30px;
            text-align: center;
        }

        .button-container {
            display: flex;
            gap: 20px;
            justify-content: center;
            margin-bottom: 40px;
            flex-wrap: wrap;
        }

        .demo-button {
            padding: 25px 50px;
            font-size: 1.5em;
            font-weight: bold;
            border: none;
            border-radius: 20px;
            cursor: pointer;
            transition: all 0.3s ease;
            box-shadow: 0 5px 20px rgba(0,0,0,0.2);
            position: relative;
            overflow: hidden;
        }

        .btn-fast {
            background: linear-gradient(135deg, #51cf66 0%, #37b24d 100%);
            color: white;
        }

        .btn-slow {
            background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
            color: white;
        }

        .demo-button:hover:not(:disabled) {
            transform: translateY(-5px);
            box-shadow: 0 10px 30px rgba(0,0,0,0.3);
        }

        .demo-button:disabled {
            opacity: 0.6;
            cursor: not-allowed;
        }

        .demo-button::before {
            content: attr(data-emoji);
            font-size: 1.2em;
            margin-right: 10px;
        }

        /* 可视化区域 */
        .visual-area {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 30px;
            margin-bottom: 30px;
        }

        .visual-panel {
            background: #f8f9fa;
            border-radius: 15px;
            padding: 30px;
            min-height: 400px;
        }

        .panel-header {
            text-align: center;
            margin-bottom: 25px;
            padding-bottom: 15px;
            border-bottom: 3px solid #dee2e6;
        }

        .panel-title {
            font-size: 1.8em;
            margin-bottom: 10px;
        }

        .panel-subtitle {
            color: #666;
            font-size: 1.1em;
        }

        /* 任务卡片 */
        .task-card {
            background: white;
            border-radius: 12px;
            padding: 20px;
            margin-bottom: 15px;
            box-shadow: 0 3px 10px rgba(0,0,0,0.1);
            position: relative;
            overflow: hidden;
        }

        .task-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 12px;
        }

        .task-name {
            font-size: 1.2em;
            font-weight: bold;
            color: #333;
        }

        .task-status {
            padding: 5px 15px;
            border-radius: 20px;
            font-size: 0.9em;
            font-weight: bold;
        }

        .status-waiting {
            background: #e9ecef;
            color: #6c757d;
        }

        .status-running {
            background: #4dabf7;
            color: white;
            animation: blink 1s infinite;
        }

        .status-done {
            background: #51cf66;
            color: white;
        }

        @keyframes blink {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.6; }
        }

        .task-progress {
            height: 8px;
            background: #e9ecef;
            border-radius: 10px;
            overflow: hidden;
            margin-bottom: 10px;
        }

        .progress-bar {
            height: 100%;
            background: linear-gradient(90deg, #667eea, #764ba2);
            width: 0%;
            transition: width 0.3s ease;
            border-radius: 10px;
        }

        .task-time {
            color: #666;
            font-size: 0.95em;
        }

        /* 结果面板 */
        .result-panel {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border-radius: 15px;
            padding: 25px;
            text-align: center;
            margin-top: 20px;
        }

        .result-label {
            font-size: 1.1em;
            margin-bottom: 10px;
            opacity: 0.9;
        }

        .result-time {
            font-size: 3em;
            font-weight: bold;
        }

        /* 最终对比结果 */
        .final-result {
            background: white;
            border-radius: 20px;
            padding: 40px;
            box-shadow: 0 15px 50px rgba(0,0,0,0.3);
            display: none;
            margin-bottom: 30px;
        }

        .final-result.show {
            display: block;
            animation: slideUp 0.6s ease;
        }

        @keyframes slideUp {
            from {
                opacity: 0;
                transform: translateY(50px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        .final-title {
            font-size: 2.5em;
            color: #667eea;
            text-align: center;
            margin-bottom: 40px;
        }

        .final-stats {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 25px;
            margin-bottom: 40px;
        }

        .stat-card {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 30px;
            border-radius: 15px;
            text-align: center;
        }

        .stat-value {
            font-size: 3.5em;
            font-weight: bold;
            margin-bottom: 10px;
        }

        .stat-label {
            font-size: 1.2em;
            opacity: 0.9;
        }

        .conclusion {
            background: #d4edda;
            border: 3px solid #28a745;
            border-radius: 15px;
            padding: 30px;
            text-align: center;
        }

        .conclusion-text {
            font-size: 1.8em;
            color: #155724;
            line-height: 1.8;
            font-weight: bold;
        }

        .conclusion-emoji {
            font-size: 4em;
            margin-bottom: 20px;
        }

        @media (max-width: 1024px) {
            .comparison-row,
            .visual-area {
                grid-template-columns: 1fr;
            }

            .final-stats {
                grid-template-columns: 1fr;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🚀 协程到底有多快?</h1>
            <p class="subtitle">用<span class="highlight">去银行办事</span>的例子说明白!</p>
        </div>

        <!-- 比喻说明 -->
        <div class="explanation-card">
            <div class="explanation-title">💡 生活中的例子</div>

            <div class="comparison-row">
                <div class="comparison-box box-sequential">
                    <span class="emoji">🐢</span>
                    <h3>顺序执行</h3>
                    <p>就像银行<strong>只开一个窗口</strong></p>
                    <p>三个人排队办业务:</p>
                    <p>👨 第1个人办完 (1秒)</p>
                    <p>👩 第2个人才能开始 (1.5秒)</p>
                    <p>👴 第3个人最后办 (0.8秒)</p>
                    <div class="time-display">总共: 3.3秒</div>
                </div>

                <div class="comparison-box box-concurrent">
                    <span class="emoji">⚡</span>
                    <h3>并发执行 (协程)</h3>
                    <p>就像银行<strong>开三个窗口</strong></p>
                    <p>三个人同时办业务:</p>
                    <p>👨👩👴 三人一起开始</p>
                    <p>最快的先办完</p>
                    <p>等最慢的那个就行</p>
                    <div class="time-display">总共: 1.5秒</div>
                </div>
            </div>

            <div style="text-align: center; font-size: 1.5em; color: #667eea; font-weight: bold;">
                ✨ 并发执行快了 <span style="color: #28a745;">54%</span>,这就是协程的魔力!
            </div>
        </div>

        <!-- 实际演示 -->
        <div class="demo-section">
            <div class="demo-title">🎮 点击按钮,看真实效果!</div>

            <div class="button-container">
                <button class="demo-button btn-fast" data-emoji="⚡" onclick="runConcurrent()" id="btnConcurrent">
                    并发执行 (三个窗口)
                </button>
                <button class="demo-button btn-slow" data-emoji="🐢" onclick="runSequential()" id="btnSequential">
                    顺序执行 (一个窗口)
                </button>
            </div>

            <div class="visual-area">
                <!-- 并发执行可视化 -->
                <div class="visual-panel">
                    <div class="panel-header">
                        <div class="panel-title">⚡ 并发执行</div>
                        <div class="panel-subtitle">三个任务同时开始</div>
                    </div>

                    <div class="task-card">
                        <div class="task-header">
                            <div class="task-name">🌐 HTTP请求</div>
                            <div class="task-status status-waiting" id="concurrent-status-1">等待</div>
                        </div>
                        <div class="task-progress">
                            <div class="progress-bar" id="concurrent-progress-1"></div>
                        </div>
                        <div class="task-time" id="concurrent-time-1">需要 1000ms</div>
                    </div>

                    <div class="task-card">
                        <div class="task-header">
                            <div class="task-name">💾 数据库查询</div>
                            <div class="task-status status-waiting" id="concurrent-status-2">等待</div>
                        </div>
                        <div class="task-progress">
                            <div class="progress-bar" id="concurrent-progress-2"></div>
                        </div>
                        <div class="task-time" id="concurrent-time-2">需要 1500ms</div>
                    </div>

                    <div class="task-card">
                        <div class="task-header">
                            <div class="task-name">⚡ Redis操作</div>
                            <div class="task-status status-waiting" id="concurrent-status-3">等待</div>
                        </div>
                        <div class="task-progress">
                            <div class="progress-bar" id="concurrent-progress-3"></div>
                        </div>
                        <div class="task-time" id="concurrent-time-3">需要 800ms</div>
                    </div>

                    <div class="result-panel">
                        <div class="result-label">总执行时间</div>
                        <div class="result-time" id="concurrentResult">-</div>
                    </div>
                </div>

                <!-- 顺序执行可视化 -->
                <div class="visual-panel">
                    <div class="panel-header">
                        <div class="panel-title">🐢 顺序执行</div>
                        <div class="panel-subtitle">一个接一个执行</div>
                    </div>

                    <div class="task-card">
                        <div class="task-header">
                            <div class="task-name">🌐 HTTP请求</div>
                            <div class="task-status status-waiting" id="sequential-status-1">等待</div>
                        </div>
                        <div class="task-progress">
                            <div class="progress-bar" id="sequential-progress-1"></div>
                        </div>
                        <div class="task-time" id="sequential-time-1">需要 1000ms</div>
                    </div>

                    <div class="task-card">
                        <div class="task-header">
                            <div class="task-name">💾 数据库查询</div>
                            <div class="task-status status-waiting" id="sequential-status-2">等待</div>
                        </div>
                        <div class="task-progress">
                            <div class="progress-bar" id="sequential-progress-2"></div>
                        </div>
                        <div class="task-time" id="sequential-time-2">需要 1500ms</div>
                    </div>

                    <div class="task-card">
                        <div class="task-header">
                            <div class="task-name">⚡ Redis操作</div>
                            <div class="task-status status-waiting" id="sequential-status-3">等待</div>
                        </div>
                        <div class="task-progress">
                            <div class="progress-bar" id="sequential-progress-3"></div>
                        </div>
                        <div class="task-time" id="sequential-time-3">需要 800ms</div>
                    </div>

                    <div class="result-panel">
                        <div class="result-label">总执行时间</div>
                        <div class="result-time" id="sequentialResult">-</div>
                    </div>
                </div>
            </div>
        </div>

        <!-- 最终对比结果 -->
        <div class="final-result" id="finalResult">
            <div class="final-title">🎉 对比结果出炉!</div>

            <div class="final-stats">
                <div class="stat-card">
                    <div class="stat-value" id="statConcurrent">-</div>
                    <div class="stat-label">并发执行</div>
                </div>
                <div class="stat-card">
                    <div class="stat-value" id="statSequential">-</div>
                    <div class="stat-label">顺序执行</div>
                </div>
                <div class="stat-card">
                    <div class="stat-value" id="statSaved">-</div>
                    <div class="stat-label">节省时间</div>
                </div>
            </div>

            <div class="conclusion">
                <div class="conclusion-emoji">🚀</div>
                <div class="conclusion-text" id="conclusionText">
                    协程让程序快了XX%!<br>
                    就像银行多开窗口,效率大幅提升!
                </div>
            </div>
        </div>
    </div>

    <script>
        let concurrentTime = 0;
        let sequentialTime = 0;
        let isRunning = false;

        // 并发执行
        async function runConcurrent() {
            if (isRunning) return;
            isRunning = true;
            disableButtons();

            resetTasks('concurrent');
            document.getElementById('concurrentResult').textContent = '执行中...';

            const startTime = Date.now();

            // 模拟三个任务同时开始
            const tasks = [
                animateTask('concurrent', 1, 1000),
                animateTask('concurrent', 2, 1500),
                animateTask('concurrent', 3, 800)
            ];

            await Promise.all(tasks);

            // 调用真实的服务器接口
            try {
                const response = await fetch('http://127.0.0.1:8080/async-tasks');
                const realTime = Date.now() - startTime;
                concurrentTime = realTime;
                document.getElementById('concurrentResult').textContent = realTime + 'ms';

                checkAndShowResult();
            } catch (error) {
                document.getElementById('concurrentResult').textContent = '连接失败';
                alert('请确保服务器正在运行:php server.php start');
            }

            isRunning = false;
            enableButtons();
        }

        // 顺序执行
        async function runSequential() {
            if (isRunning) return;
            isRunning = true;
            disableButtons();

            resetTasks('sequential');
            document.getElementById('sequentialResult').textContent = '执行中...';

            const startTime = Date.now();

            // 一个接一个执行
            await animateTask('sequential', 1, 1000);
            await animateTask('sequential', 2, 1500);
            await animateTask('sequential', 3, 800);

            // 调用真实的服务器接口
            try {
                const response = await fetch('http://127.0.0.1:8080/sequential');
                const realTime = Date.now() - startTime;
                sequentialTime = realTime;
                document.getElementById('sequentialResult').textContent = realTime + 'ms';

                checkAndShowResult();
            } catch (error) {
                document.getElementById('sequentialResult').textContent = '连接失败';
                alert('请确保服务器正在运行:php server.php start');
            }

            isRunning = false;
            enableButtons();
        }

        // 动画模拟任务执行
        function animateTask(type, taskNum, duration) {
            return new Promise(resolve => {
                const statusEl = document.getElementById(`${type}-status-${taskNum}`);
                const progressEl = document.getElementById(`${type}-progress-${taskNum}`);
                const timeEl = document.getElementById(`${type}-time-${taskNum}`);

                statusEl.textContent = '执行中';
                statusEl.className = 'task-status status-running';

                let elapsed = 0;
                const interval = setInterval(() => {
                    elapsed += 50;
                    const progress = Math.min((elapsed / duration) * 100, 100);
                    progressEl.style.width = progress + '%';
                    timeEl.textContent = `已执行 ${elapsed}ms / ${duration}ms`;

                    if (elapsed >= duration) {
                        clearInterval(interval);
                        statusEl.textContent = '完成';
                        statusEl.className = 'task-status status-done';
                        timeEl.textContent = `✓ 完成,耗时 ${duration}ms`;
                        resolve();
                    }
                }, 50);
            });
        }

        // 重置任务状态
        function resetTasks(type) {
            for (let i = 1; i <= 3; i++) {
                const statusEl = document.getElementById(`${type}-status-${i}`);
                const progressEl = document.getElementById(`${type}-progress-${i}`);
                const durations = [1000, 1500, 800];

                statusEl.textContent = '等待';
                statusEl.className = 'task-status status-waiting';
                progressEl.style.width = '0%';
                document.getElementById(`${type}-time-${i}`).textContent = `需要 ${durations[i-1]}ms`;
            }
        }

        // 检查并显示最终结果
        function checkAndShowResult() {
            if (concurrentTime > 0 && sequentialTime > 0) {
                const saved = sequentialTime - concurrentTime;
                const percentage = ((saved / sequentialTime) * 100).toFixed(1);

                document.getElementById('statConcurrent').textContent = concurrentTime + 'ms';
                document.getElementById('statSequential').textContent = sequentialTime + 'ms';
                document.getElementById('statSaved').textContent = saved + 'ms';
                document.getElementById('conclusionText').innerHTML = `
                    协程让程序快了 <span style="color: #28a745; font-size: 1.2em;">${percentage}%</span>!<br>
                    就像银行多开窗口,效率大幅提升!
                `;

                const resultEl = document.getElementById('finalResult');
                resultEl.classList.add('show');

                setTimeout(() => {
                    resultEl.scrollIntoView({ behavior: 'smooth', block: 'center' });
                }, 300);
            }
        }

        function disableButtons() {
            document.getElementById('btnConcurrent').disabled = true;
            document.getElementById('btnSequential').disabled = true;
        }

        function enableButtons() {
            document.getElementById('btnConcurrent').disabled = false;
            document.getElementById('btnSequential').disabled = false;
        }
    </script>
</body>
</html>
29 0 0
0个评论

贵哥的编程之路

800
积分
0
获赞数
0
粉丝数
2025-07-11 加入
🔝