前端异步编程实战:从json-server数据加载到DeepSeek大模型接口调用
深入理解异步与同步,掌握fetch请求、数据渲染和大模型API调用的完整流程
一、项目背景
在学习前端开发时,我们经常需要与后端交互。本文通过两个实战案例,带你彻底掌握:
- 使用
json-server模拟后端API,实现数据的加载与渲染 - 调用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('失败'));
七、总结与最佳实践
核心要点
- fetch 是异步的:需要用 await 或 then 获取结果
- async 函数返回 Promise:可以用 await 调用
- HTTP 请求体必须是字符串:记得 JSON.stringify()
- API Key 不能写在前端代码中:使用环境变量
- 总是处理错误:try-catch 或 .catch()
学习建议
- 先理解同步/异步的概念,再学 Promise
- 掌握 async/await 后,日常开发优先使用
- 多练习 fetch 请求,熟悉 HTTP 协议
- 调用真实 API 能加深理解,推荐使用 DeepSeek(便宜又好用)
📌 本文代码可直接复制运行,建议动手实践,加深理解!