一次搞懂 Bun、TypeScript、异步编程,并亲手调用 DeepSeek 大模型
📖 前言
最近 Anthropic 收购了 Bun,并将其作为 Claude Code 的底层运行时,让这个年轻的 JavaScript 运行时再次成为技术圈的焦点。作为一个前端/全栈开发者,我也踏上了 Bun + TypeScript 的学习之旅。
本文是我从零开始学习 Bun 和 TypeScript 的完整记录——从最基础的类型约束,到异步编程,再到最终调用 DeepSeek 大模型 API,每一步都有代码和思考。希望能帮助同样在探索这条路的你 😊
一、为什么是 Bun?
1.1 Bun 是什么?
bun 是比 node 更快,开箱即用,零配置的 js/ts 运行时 + 包管理器
简单来说,Bun 就是 Node.js 的优化升级版,它集成了:
| 功能 | Node.js | Bun |
|---|---|---|
| JS 运行时 | ✅ | ✅(更快) |
| TS 原生支持 | ❌ 需要 ts-node/tsx | ✅ 开箱即用 |
| 包管理器 | npm/yarn/pnpm | ✅ 内置(兼容 npm) |
| 打包器 | webpack/esbuild | ✅ 内置 |
| 测试框架 | jest/vitest | ✅ 内置 |
1.2 安装 Bun
Windows 用户打开 PowerShell,一行命令搞定:
powershell -c "irm bun.sh/install/windows | iex"
安装完成后验证:
bun --version
# 输出类似: 1.3.14
二、TypeScript:给 JavaScript 装上类型"安全带"
2.1 JS 的痛:弱类型的坑
JavaScript 是弱类型语言,这给它带来了灵活性,但也是无数 Bug 的温床。来看看这段代码:
<!-- a.html - 浏览器中的陷阱 -->
<input type="text" id="ipt">
<script>
const ipt = document.getElementById("ipt");
ipt.addEventListener("change", function(event) {
console.log(event.target.value, typeof event.target.value);
// 你输入 123,它打印的是 "123" string 而不是 123 number!
})
</script>
核心问题:浏览器 input 输入的值,我们以为是数字,实际上全是字符串!
再看一个更隐蔽的例子:
let a = 1;
let b = "2";
let result = a + b; // "12" — 字符串拼接,不是数学加法!
console.log(result); // "12" 😱
+ 运算符在 JS 中既是数学加法又是字符串拼接。当数字遇上字符串,JS 会默默把数字转成字符串然后拼接——不报错、不警告,Bug 就这样潜伏在系统里,可能很久以后才被发现。
2.2 TypeScript 登场:在编译期揪出错误
TypeScript 是微软开发的 JavaScript 超集,核心价值就一句话:
在写代码的时候就发现类型错误,而不是等运行时爆炸 💥
// 1.ts - 基础类型注解
const nickname: string = "9527";
const age: number = 18; // 如果你写成 "18",TS 直接标红!
console.log(`我是${nickname},我今年${age}岁了`);
// 2.ts - 函数类型约束
function add(a: number, b: number): number {
return a + b; // TS 确保 a 和 b 都是数字,+ 就是数学加法
}
let a = 1;
let b = "2";
// add(a, b); // ❌ TS 编译报错:类型"string"的参数不能赋给类型"number"的参数
// 正确的做法:显式类型转换
let c: number = add(a, Number(b)); // ✅ 明确告诉 TS:我就是要转成数字
console.log(c); // 3
TypeScript 的价值链:
写代码 → TS 编译器检查类型 → 发现潜在错误 → 修复 → 编译成 JS → 安全运行
↓
这才是 AI Agent 开发的标配!
💡 小贴士:TS 的编译过程是 静态类型检查,编译成 JS 后类型注解会被完全擦除。它不会影响运行时性能,只在你写代码时保护你。
三、异步编程:Promise 和 async/await
3.1 封装一个 sleep 函数
在实际开发中,我们经常需要"等一会儿再执行"。JS 是单线程的,不能用传统的 Thread.sleep(),而是用 Promise + setTimeout 来实现:
// 3.js - 封装 sleep 函数
function sleep(t) {
// Promise 是 ES6 提供的异步解决方案 —— "许下承诺"
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(); // t 毫秒后,"承诺"兑现
}, t);
});
}
async function main() {
console.log('--start--');
await sleep(2000); // 🔑 await 让异步任务"看起来像"同步执行
console.log('--end--'); // 2秒后才会打印
}
main();
3.2 执行流程可视化
时间轴 →
─────────────────────────────────────────────────────
│ start │ 等待 2000ms... │ end │
│ (立即打印) │ (sleep 阻塞) │ (2秒后打印) │
─────────────────────────────────────────────────────
3.3 核心概念速记
| 概念 | 一句话解释 |
|---|---|
| Promise | 一个"未来才会完成"的任务的容器 |
| async | 声明一个函数是异步的,返回值自动包装成 Promise |
| await | 等待一个 Promise 完成,拿到结果后再继续往下走 |
| resolve | 承诺兑现,任务成功完成 |
| reject | 承诺破裂,任务失败了 |
四、实战:用 axios 调用 DeepSeek 大模型
理论学完了,来点真枪实弹的!我们用 Bun + TypeScript + axios 调用 DeepSeek API,实现一个最简单的 AI 对话。
4.1 项目初始化
# 创建项目目录
mkdir axios-demo && cd axios-demo
# 用 bun 初始化项目(比 npm init 快得多!)
bun init
# 安装依赖
bun add axios dotenv
bun add -d @types/bun
项目结构:
axios-demo/
├── .env # API Key 等敏感信息
├── .gitignore # 忽略 node_modules 等
├── package.json # 项目配置
├── tsconfig.json # TypeScript 配置
├── bun.lock # 依赖锁定文件
├── index.ts # 主程序入口
└── node_modules/ # 依赖包
4.2 配置环境变量
# .env — 千万不要提交到 Git!
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1/chat/completions
DEEPSEEK_API_KEY=sk-你的API密钥
⚠️
.gitignore中一定要包含.env,保护好你的 API Key!
4.3 核心代码:调用 LLM
// index.ts — HTTP 请求 LLM 接口
import axios from "axios";
import dotenv from "dotenv";
dotenv.config(); // 加载 .env 中的环境变量
async function chat() {
try {
const res = await axios.post(
// 📍 请求行:URL + HTTP Method
`${process.env.DEEPSEEK_BASE_URL}`,
// 📦 请求体:发给 AI 的数据
{
model: 'deepseek-v4-flash',
messages: [
{
role: "user",
content: "你好,介绍一下 Bun",
},
],
},
// 🔐 请求头:身份认证
{
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${process.env.DEEPSEEK_API_KEY}`,
},
}
);
// axios 默认在响应数据外包装了一层 data
console.log(res.data.choices[0].message.content);
} catch (err: any) {
// LLM 可能出错:网络超时、API Key 错误、模型繁忙...
console.log(err.message);
}
}
chat();
4.4 运行!
bun run index.ts
你会看到 DeepSeek 返回的关于 Bun 的介绍——恭喜,你已经成功调用了大模型 API!🎉
4.5 HTTP 请求结构解析
理解一次 API 调用的完整结构非常重要,这是和后端/AI 通信的基础:
┌──────────────────────────────────────────────┐
│ HTTP 请求 │
├──────────────────────────────────────────────┤
│ 请求行 │
│ POST /v1/chat/completions HTTP/1.1 │
├──────────────────────────────────────────────┤
│ 请求头 (Headers) │
│ Content-Type: application/json │
│ Authorization: Bearer sk-xxx... │
├──────────────────────────────────────────────┤
│ 请求体 (Body) │
│ { │
│ "model": "deepseek-v4-flash", │
│ "messages": [{ │
│ "role": "user", │
│ "content": "你好,介绍一下 Bun" │
│ }] │
│ } │
└──────────────────────────────────────────────┘
| 部分 | 作用 | 注意事项 |
|---|---|---|
| 请求行 | URL + Method | GET 请求有长度限制,POST 无限制 |
| 请求头 | 元信息 + 认证 | API Key 在这里,不要在 URL 里明文传递 |
| 请求体 | 实际数据 | 图片上传、大段文本都用 POST 请求体 |
| 响应体 | 返回数据 | 从 choices[0].message.content 取 AI 回复 |
4.6 为什么用 axios 而不是 fetch?
// fetch 是浏览器原生 API,Bun 也支持
// 但 axios 在企业级开发中更受欢迎,因为:
// ✅ 自动 JSON 解析
// ✅ 请求/响应拦截器
// ✅ 更好的错误处理(非 2xx 状态码自动抛异常)
// ✅ 请求超时设置
// ✅ 请求取消功能
// ✅ 上传/下载进度监听
五、避坑指南:我在学习中踩过的坑
坑 1:环境变量未加载
// ❌ 忘记调用 dotenv.config(),process.env.xxx 全是 undefined
// ✅ 在文件顶部尽早调用
import dotenv from "dotenv";
dotenv.config();
坑 2:API Key 泄露到 Git
# ❌ .env 文件被提交到仓库
git add .env
# ✅ 确保 .gitignore 包含 .env
echo ".env" >> .gitignore
坑 3:GET vs POST 选错
GET 请求:URL 参数明文,有长度限制 → 不适合传 API Key 和大段文本
POST 请求:数据在请求体,无长度限制 → 调用 LLM 的正确选择 ✅
坑 4:TypeScript 编译错误被忽略
// tsconfig.json 中 strict: true 是你的好朋友
// 它能帮你发现:
// - 可能为 null/undefined 的值
// - 隐式 any 类型
// - 未使用的变量
// 不要关闭 strict!
六、技术全景:我们学到了什么
graph TD
A[JavaScript 基础] --> B[TypeScript 类型系统]
A --> C[异步编程 Promise/async-await]
B --> D[Bun 运行时]
C --> D
D --> E[HTTP 请求 axios]
E --> F[调用 DeepSeek API]
F --> G[AI Agent 应用 🎯]
| 层级 | 技术 | 掌握程度 |
|---|---|---|
| 语言层 | JavaScript → TypeScript | 类型注解、函数类型约束 |
| 异步层 | Promise + async/await | sleep 封装、异步流程控制 |
| 运行时 | Bun | 包管理、原生 TS 支持、零配置 |
| 网络层 | axios + HTTP 协议 | 请求行/头/体的完整理解 |
| 应用层 | DeepSeek API | 模型调用、环境变量管理 |
七、下一步学习路线
掌握了这些基础后,你可以继续深入:
- 流式响应 (Streaming) :大模型逐字返回,用户体验更好
- 多轮对话:把历史消息传入 messages 数组,实现上下文记忆
- Function Calling:让 AI 调用你的函数,实现真正的 Agent
- 错误重试机制:网络抖动时自动重试,提升稳定性
- Bun 全栈开发:用 Bun 内置的 HTTP Server 写 API 服务
🎁 总结
这篇文章我们从 Bun 的安装开始,系统学习了:
- ✅ Bun:比 Node.js 更快,原生支持 TypeScript
- ✅ TypeScript:用类型约束消灭隐藏 Bug,AI Agent 开发标配
- ✅ 异步编程:Promise + async/await,让异步代码像同步一样清晰
- ✅ HTTP 协议:理解请求行/请求头/请求体的结构
- ✅ 实战调用:用 axios 成功调用 DeepSeek API
从最基础的 const name: string = "9527" 到调用大模型 API,这条路并不长。关键是每一步都理解透彻,而不是囫囵吞枣 🌟
🔗 参考资料
写于 2026 年 6 月 · 学习永不止步 🚀