前端异步编程完全指南:从json-server到DeepSeek大模型接口调用

前端异步编程实战:从json-server数据加载到DeepSeek大模型接口调用

深入理解异步与同步,掌握fetch请求、数据渲染和大模型API调用的完整流程

一、项目背景

在学习前端开发时,我们经常需要与后端交互。本文通过两个实战案例,带你彻底掌握:

  1. 使用 json-server 模拟后端API,实现数据的加载与渲染
  2. 调用DeepSeek大模型API,理解HTTP请求的完整流程

二、异步 vs 同步:核心概念

什么是同步?

javascript

arduino 复制代码
console.log('第一步')
console.log('第二步')
console.log('第三步')
// 输出:第一步 → 第二步 → 第三步(按顺序执行)

同步的特点:代码从上到下依次执行,前面的代码没执行完,后面的代码就得等着。

什么是异步?

javascript

javascript 复制代码
console.log('1. 开始')

setTimeout(() => {
    console.log('2. 延迟执行')
}, 1000)

console.log('3. 结束')

// 输出:1. 开始 → 3. 结束 → 2. 延迟执行

异步的特点:某些任务需要等待(如网络请求、定时器),JS不会傻等,而是先执行后面的代码。

为什么需要异步?

  • 网络请求可能需要几秒钟
  • 如果同步等待,页面会"卡死",用户无法操作
  • 异步让页面保持响应,请求完成后通过回调处理结果

三、案例一:json-server 模拟后端

项目结构

text

bash 复制代码
project/
├── data.json      # 数据文件
├── main.js        # 前端逻辑
├── index.html     # 页面结构
└── package.json   # 依赖配置

1. 数据文件 data.json

json

json 复制代码
{
  "friends": [
    {
      "id": 1,
      "name": "name1",
      "age": 18
    },
    {
      "id": 2,
      "name": "name2",
      "age": 10
    }
  ]
}

2. package.json 配置

json

json 复制代码
{
  "name": "backend",
  "version": "1.0.0",
  "scripts": {
    "dev": "json-server --watch data.json --port 3000"
  },
  "dependencies": {
    "json-server": "1.0.0-beta.15"
  }
}

启动命令npm run dev

启动后访问 http://localhost:3000/friends 即可获取数据。

3. HTML 结构

html

xml 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>好友列表</title>
</head>
<body>
  <header>
    <h1>前端发送HTTP请求</h1>
  </header>
  <main>
    <table border="1">
      <thead>
        <tr>
          <th>ID</th>
          <th>姓名</th>
          <th>年龄</th>
        </tr>
      </thead>
      <tbody>
        <!-- JS 动态渲染数据 -->
      </tbody>
    </table>
  </main>
  <script src="./main.js"></script>
</body>
</html>

4. main.js 核心逻辑

async/await 写法(推荐)

javascript

javascript 复制代码
// 1. 加载数据(异步变同步写法)
async function loadData() {
    const endpoint = 'http://localhost:3000/friends';
    const res = await fetch(endpoint);      // 等待响应返回
    const data = await res.json();          // 解析JSON
    return data;
}

// 2. 渲染数据到表格
function renderData(friends) {
    const oBody = document.querySelector('table tbody');
    
    if (friends.length > 0) {
        oBody.innerHTML = friends.map(friend => `
            <tr>
                <td>${friend.id}</td>
                <td>${friend.name}</td>
                <td>${friend.age}</td>
            </tr>
        `).join('');
    }
}

// 3. 初始化
async function init() {
    console.log('init start');
    const friends = await loadData();  // 等待数据返回
    console.log(friends);
    renderData(friends);
    console.log('init end');
}

init();
对比:Promise.then() 写法

javascript

ini 复制代码
function loadDataWithPromise() {
    const endpoint = 'http://localhost:3000/friends';
    
    fetch(endpoint)
        .then(res => res.json())
        .then(data => {
            console.log(data);
            renderData(data);
        })
        .catch(err => console.error('请求失败', err));
}

async/await vs Promise.then() 对比

特性 async/await Promise.then()
代码可读性 像同步代码,更清晰 回调嵌套,较混乱
错误处理 try-catch .catch()
调试体验 更好 一般
适用场景 复杂异步流程 简单链式调用

四、案例二:调用DeepSeek大模型API

HTTP 请求的组成

一个完整的HTTP请求包含三部分:

text

markdown 复制代码
1. 请求行:URL + 方法(POST/GET) + HTTP版本
2. 请求头:Headers(包含认证信息、内容类型等)
3. 请求体:Body(POST请求携带的数据)

完整代码实现

javascript

javascript 复制代码
// 请求地址(请求行)
const endpoint = 'https://api.deepseek.com/chat/completions';

// 请求头(Headers)
const headers = {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${process.env.DEEPSEEK_API_KEY}`
};

// 请求体(Body)
const payload = {
    model: 'deepseek-chat',  // 模型名称
    messages: [
        {
            role: 'system',
            content: 'You are a helpful assistant'
        },
        {
            role: 'user',
            content: '你好,DeepSeek'
        }
    ]
};

// 发送请求
async function callDeepSeek() {
    try {
        const response = await fetch(endpoint, {
            method: 'POST',           // 请求方法
            headers: headers,         // 请求头
            body: JSON.stringify(payload)  // 请求体(必须转成字符串)
        });
        
        const data = await response.json();
        console.log(data);
        
        // 提取AI的回复内容
        const reply = data.choices[0].message.content;
        document.getElementById('reply').innerHTML = reply;
        
    } catch (err) {
        console.error('请求失败:', err);
        document.getElementById('reply').innerHTML = '请求失败,请检查网络或API Key';
    }
}

callDeepSeek();

关键知识点

1. 为什么 body 要用 JSON.stringify()?

javascript

javascript 复制代码
// ❌ 错误:HTTP 只能传输字符串,不能传输对象
body: payload  // payload 是一个对象

// ✅ 正确:转成 JSON 字符串
body: JSON.stringify(payload)
2. API Key 的安全性

javascript

javascript 复制代码
// ❌ 绝对不要这样写(会暴露密钥)
Authorization: 'Bearer sk-xxxxxx'

// ✅ 使用环境变量
Authorization: `Bearer ${process.env.DEEPSEEK_API_KEY}`

在项目根目录创建 .env 文件:

text

ini 复制代码
DEEPSEEK_API_KEY=你的真实API密钥
3. 响应数据结构解析

javascript

kotlin 复制代码
// DeepSeek 返回的数据结构
{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "你好!有什么我可以帮助你的吗?"
      }
    }
  ]
}

// 提取回复内容
const reply = data.choices[0].message.content;

五、完整页面示例(集成大模型)

html

xml 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>DeepSeek AI 助手</title>
    <style>
        body { font-family: Arial; max-width: 800px; margin: 50px auto; }
        #reply { 
            background: #f0f0f0; 
            padding: 20px; 
            border-radius: 10px;
            min-height: 100px;
        }
        button { 
            background: #1890ff; 
            color: white; 
            border: none; 
            padding: 10px 20px;
            border-radius: 5px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <h1>DeepSeek AI 助手</h1>
    <button onclick="callDeepSeek()">点击提问</button>
    <h3>AI回复:</h3>
    <div id="reply">等待提问...</div>

    <script>
        const endpoint = 'https://api.deepseek.com/chat/completions';
        const headers = {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${process.env.DEEPSEEK_API_KEY}`
        };

        async function callDeepSeek() {
            document.getElementById('reply').innerHTML = '思考中...';
            
            const payload = {
                model: 'deepseek-chat',
                messages: [
                    { role: 'system', content: 'You are a helpful assistant' },
                    { role: 'user', content: '你好,DeepSeek!请介绍一下你自己' }
                ]
            };

            try {
                const response = await fetch(endpoint, {
                    method: 'POST',
                    headers: headers,
                    body: JSON.stringify(payload)
                });
                const data = await response.json();
                document.getElementById('reply').innerHTML = data.choices[0].message.content;
            } catch (err) {
                document.getElementById('reply').innerHTML = '请求失败:' + err.message;
            }
        }
    </script>
</body>
</html>

六、面试常见问题

Q1: 为什么 async/await 要放在 try-catch 中?

javascript

javascript 复制代码
// ❌ 错误:没有错误处理
async function bad() {
    const res = await fetch('wrong-url');
}

// ✅ 正确:使用 try-catch
async function good() {
    try {
        const res = await fetch('wrong-url');
    } catch (err) {
        console.error('请求失败', err);
    }
}

Q2: Promise 的三个状态是什么?

  • pending(进行中):初始状态
  • fulfilled(已成功):操作成功
  • rejected(已失败):操作失败

javascript

arduino 复制代码
const promise = fetch(url);
console.log(promise);  // Promise {<pending>}

promise.then(() => console.log('成功')).catch(() => console.log('失败'));

七、总结与最佳实践

核心要点

  1. fetch 是异步的:需要用 await 或 then 获取结果
  2. async 函数返回 Promise:可以用 await 调用
  3. HTTP 请求体必须是字符串:记得 JSON.stringify()
  4. API Key 不能写在前端代码中:使用环境变量
  5. 总是处理错误:try-catch 或 .catch()

学习建议

  • 先理解同步/异步的概念,再学 Promise
  • 掌握 async/await 后,日常开发优先使用
  • 多练习 fetch 请求,熟悉 HTTP 协议
  • 调用真实 API 能加深理解,推荐使用 DeepSeek(便宜又好用)

📌 本文代码可直接复制运行,建议动手实践,加深理解!

相关推荐
用户059540174461 小时前
大模型多轮对话“失忆”踩坑实录:一次线上事故让我排查了48小时,最终靠 Playwright + Pytest 把记忆锁死
前端·css
橘子星1 小时前
前端薅数据神器 Fetch:不用翻墙,在线拿捏后端与 AI 接口
前端·后端
步步为营DotNet1 小时前
探索.NET 11:Blazor 在跨平台客户端应用开发的进阶实践
前端·asp.net·.net
Hello馒头儿1 小时前
vue3+uniapp经典hook方式实现一个更多加载的列表组件
前端·javascript·vue.js
浩风祭月1 小时前
前端错误监控方案对比:Sentry SaaS vs 自部署 vs 纯开源组合
前端·openai·ai编程
ze_juejin1 小时前
promise和try catch的比较
前端
用户573240037231 小时前
AgentForge-WX v0.3.0:12项更新 + 框架重新定位,把微信小程序AI对话的坑全填了
前端
米丘1 小时前
HTTP 传输层 TCP 三次握手 / 四次挥手
前端·网络协议·http
小lan猫1 小时前
多域 RAG 知识库:从 Vue 前端到 NestJS + PGVector 的全栈实践
前端·人工智能·typescript