各语言的异步实现方式对比

本文系统梳理 Python 和 TypeScript 中的异步关键字与机制,通过真实场景帮助理解”什么时候该用异步”以及”两者有何本质区别”。

一、先搞清楚:异步解决什么问题

在深入语法之前,先回答一个根本问题:什么场景值得用异步?

典型异步场景

场景说明阻塞方式
网络请求(HTTP / API)等待服务器响应等待网络 I/O
数据库读写等待磁盘 / 网络等待数据库 I/O
文件读写等待磁盘等待磁盘 I/O
消息队列等待消息到达等待 broker I/O
定时任务 / 延迟执行等待时间流逝时间回调
核心原则:CPU 密集的任务(矩阵运算、视频解码)不适合用异步;I/O 密集的任务才是异步的用武之地——因为在等待 I/O 时,CPU 实际上在闲置。

二、TypeScript 的异步关键字体系

TypeScript(运行在 V8 / Node.js 上)的异步基于 JavaScript 的事件循环(Event Loop)

2.1 核心关键字

async    → 声明一个异步函数,始终返回 Promise
await    → 暂停 async 函数执行,等待 Promise 解析
Promise  → 代表异步操作的最终结果(成功或失败)

2.2 三者如何配合

// 基础结构
async function fetchData() {
  // async 让函数返回 Promise
  // await 暂停当前函数,等 Promise 完成再继续
  const result = await fetch('/api/data');
  const json = await result.json();
  return json;
}
执行流程
调用 fetchData()
  → 返回 Promise(不阻塞调用者)
  → 函数内部执行到 await fetch(...) 时,当前帧暂停
  → 事件循环去处理其他任务
  → fetch 完成,Promise 决议
  → 事件循环将后续代码重新入队
  → await 之后的代码继续执行

2.3 并发控制:Promise.all vs Promise.allSettled

// 并发:所有请求同时发出,等全部完成
const [users, orders] = await Promise.all([
  fetch('/api/users').then(r => r.json()),
  fetch('/api/orders').then(r => r.json()),
]);

// 容错:即使某个请求失败,也不影响其他结果
const results = await Promise.allSettled([
  fetch('/api/a'),
  fetch('/api/b'),
  fetch('/api/c'),
]);

2.4 完整项目示例:电商数据聚合服务

场景:首页需要同时加载用户信息、商品列表、推荐内容,三者互不依赖,希望并行请求。
// types.ts
interface User { id: number; name: string; }
interface Product { id: number; title: string; price: number; }
interface Recommendation { productId: number; score: number; }

interface HomePageData {
  user: User | null;
  products: Product[];
  recommendations: Recommendation[];
}

// service.ts
async function fetchUser(): Promise<User | null> {
  const res = await fetch('/api/user/profile');
  if (!res.ok) return null;
  return res.json();
}

async function fetchProducts(): Promise<Product[]> {
  const res = await fetch('/api/products?limit=20');
  return res.json();
}

async function fetchRecommendations(userId: number): Promise<Recommendation[]> {
  const res = await fetch(`/api/recommendations?userId=${userId}`);
  return res.json();
}

// main.ts
async function loadHomePage(): Promise<HomePageData> {
  // 先拿用户(推荐需要 userId)
  const user = await fetchUser();

  // 用户存在时,商品和推荐可以并行
  if (user) {
    const [products, recs] = await Promise.all([
      fetchProducts(),
      fetchRecommendations(user.id),
    ]);
    return { user, products, recommendations: recs };
  }

  // 未登录:只并行加载商品
  const [products] = await Promise.all([
    fetchProducts(),
  ]);
  return { user: null, products, recommendations: [] };
}
这个例子的关键点
  • await 串行处理有依赖关系的步骤(先拿用户,再发推荐)
  • Promise.all 并行处理无依赖的步骤(商品和推荐同时发)
  • allSettled 替代 all 可以防止某个接口挂了导致整个页面白屏

三、Python 的异步关键字体系

Python 的异步基于 asyncio 库,从 Python 3.5 开始正式引入。

3.1 核心关键字

async    → 声明一个协程函数(coroutine),返回 coroutine 对象
await    → 暂停协程执行,等待另一个 coroutine 或 awaitable
asyncio  → Python 标准库,提供事件循环和并发原语

3.2 三者如何配合

import asyncio

# Python 中 async 函数定义时用 async,调用时必须用 await 或交给事件循环
async def fetch_data():
    # 模拟异步 I/O(真正的 asyncio 会用 aiohttp、asyncpg 等)
    await asyncio.sleep(1)  # 非阻塞地等待 1 秒
    return {"status": "done"}

# 必须通过事件循环来运行协程
result = asyncio.run(fetch_data())
与 TypeScript 的关键区别
TypeScriptPython
运行环境浏览器 / V8 / Node.js(内置事件循环)需要显式导入 asyncio,用 asyncio.run() 启动
调用 async 函数直接调用返回 Promise,可立即用 await直接调用返回 coroutine 对象,不自动运行
顶层 await支持(ES2022)不支持,必须放在 async def 内部

3.3 并发控制:asyncio.gather vs asyncio.Task

import asyncio

async def fetch_user():
    await asyncio.sleep(0.5)
    return {"id": 1, "name": "Alice"}

async def fetch_orders():
    await asyncio.sleep(0.3)
    return [{"order_id": 101, "total": 99.9}]

async def fetch_recommendations(user_id):
    await asyncio.sleep(0.8)
    return [{"product_id": 5, "score": 0.95}]

async def load_homepage():
    # gather:并发执行多个协程,返回结果列表
    user, orders = await asyncio.gather(
        fetch_user(),
        fetch_orders(),
    )

    # 依赖 user 的结果才能发起推荐请求
    recs = await fetch_recommendations(user["id"])
    return {"user": user, "orders": orders, "recs": recs}

# 运行
asyncio.run(load_homepage())

3.4 完整项目示例:异步爬虫 + 数据入库

场景:从多个 API 拉取数据,写入数据库,全部 I/O 操作异步完成。
# main.py
import asyncio
import aiohttp
import asyncpg

# 数据库连接池(async 版本)
DB_DSN = "postgres://user:pass@localhost:5432/ecommerce"

async def init_db():
    return await asyncpg.create_pool(DB_DSN)

async def fetch_session(session: aiohttp.ClientSession, url: str) -> dict:
    async with session.get(url) as resp:
        return await resp.json()

async def save_products(pool, products: list):
    async with pool.acquire() as conn:
        await conn.executemany(
            "INSERT INTO products (id, title, price) VALUES ($1, $2, $3)",
            [(p["id"], p["title"], p["price"]) for p in products],
        )

async def fetch_and_save(session: aiohttp.ClientSession, pool, url: str, table: str):
    data = await fetch_session(session, url)
    await save_products(pool, data)
    return f"[OK] {table}: {len(data)} records"

async def main():
    pool = await init_db()
    urls = [
        ("https://api.example.com/products", "products"),
        ("https://api.example.com/categories", "categories"),
        ("https://api.example.com/users", "users"),
    ]

    # 并发抓取,超时保护
    async with aiohttp.ClientSession() as session:
        tasks = [
            fetch_and_save(session, pool, url, name)
            for url, name in urls
        ]
        results = await asyncio.gather(*tasks, return_exceptions=True)

    for r in results:
        print(r if isinstance(r, str) else f"[ERR] {r}")

    await pool.close()

if __name__ == "__main__":
    asyncio.run(main())
这个例子的关键点
  • aiohttpasyncpg 都是异步原生库,不能混用同步版
  • asyncio.gather 同时发起多个 HTTP 请求,总耗时取决于最慢的那个,而不是所有耗时之和
  • return_exceptions=True 让一个请求失败不中断其他请求

四、Python vs TypeScript 的核心区别

4.1 事件循环的启动方式

// TypeScript:事件循环内置,async 函数一调用就进入队列
async function work() {
  await fetch('/api/data');
}
work(); // 直接调用即可,运行时自动管理事件循环
# Python:需要显式启动事件循环
async def work():
    await asyncio.sleep(1)

# Python 3.7+ 推荐写法
asyncio.run(work())  # 启动并运行事件循环,直到协程完成

4.2 顶层异步代码

// TypeScript:ES2022 支持顶层 await
const data = await fetch('/api/config').then(r => r.json());
console.log(data);
# Python:顶层不能用 await,必须包在 async def 里
# 错误的写法:
# data = await fetch(...)  # ❌ SyntaxError

# 正确的写法:
async def main():
    data = await fetch(...)
    print(data)

asyncio.run(main())

4.3 错误处理

// TypeScript:try/catch 直接包裹 await
try {
  const data = await fetch('/api/data').then(r => r.json());
} catch (err) {
  console.error('请求失败:', err);
}
# Python:同样用 try/except,但要注意协程未 await 时的错误
try:
    data = await fetch(...)
except Exception as e:
    print(f'请求失败: {e}')

4.4 回调地狱 vs 扁平结构(两者相同)

// 都是 async/await 解决
const user = await getUser();
const orders = await getOrders(user.id);
# 完全对称
user = await get_user()
orders = await get_orders(user["id"])

4.5 生态差异

维度TypeScript / Node.jsPython
HTTP 客户端fetch(内置)、axiosnode-fetchaiohttphttpx(需安装)
数据库驱动pgmongooseprismaasyncpgdatabasessqlalchemy.ext.asyncio
Web 框架ExpressFastifyNestJSFastAPIStarlette
ORMPrismaTypeORMSQLAlchemy 2.0 async

五、实际项目中的选择建议

用 TypeScript 异步的场景

  • 前端 React/Vue 应用:调用后端 API、处理流式数据
  • Node.js 后端:高并发 I/O 服务(API 网关、实时推送、微服务编排)
  • 全栈项目:前后端共享类型定义,异步逻辑统一风格

用 Python 异步的场景

  • 异步爬虫和数据采集
  • 需要结合 AI/数据处理(Python 生态优势)的 I/O 密集型服务
  • 用 FastAPI 构建高性能异步 Web 服务

两者混用的常见模式

前端(TypeScript async/await)
    ↓ HTTP 请求
后端(Python FastAPI + asyncio)
    ↓ 异步查询
数据库(asyncpg / SQLAlchemy async)

六、速查表

概念TypeScriptPython
异步函数声明async function f(){}async def f():
等待结果await promiseawait coroutine
并发执行多个Promise.all([a, b])asyncio.gather(a, b)
容错并发Promise.allSettled([...])asyncio.gather(..., return_exceptions=True)
启动运行运行时自动asyncio.run(main())
延迟执行setTimeout(fn, ms)await asyncio.sleep(seconds)
取消操作AbortControllertask.cancel()

七、一个对比练习

同一需求:并发请求 3 个接口,任一失败则返回错误,最后把所有结果合并。

TypeScript 实现

async function mergeAll(): Promise<Record<string, unknown>> {
  const [users, products, orders] = await Promise.all([
    fetch('/api/users').then(r => r.json()),
    fetch('/api/products').then(r => r.json()),
    fetch('/api/orders').then(r => r.json()),
  ]);
  return { users, products, orders };
}

Python 实现

async def merge_all() -> dict:
    users, products, orders = await asyncio.gather(
        fetch_json('/api/users'),
        fetch_json('/api/products'),
        fetch_json('/api/orders'),
    )
    return {"users": users, "products": products, "orders": orders}
两者逻辑完全对称,区别只在语法和启动方式。掌握一个后,另一个上手极快。

适用于 Python 3.11+ 和 TypeScript 5.x

发表评论