前言:在编程的世界里,JavaScript(JS)和 Python 就像两台性格迥异的跑车。JS 生来就在浏览器里"狂奔",必须同时处理点击、动画和网络请求;而 Python 则是从实验室里出来的"老教授",严谨、同步,直到后来才为了应对高并发学会了"多线程"和"异步"。
虽然它们现在都用 async 和 await 这两个单词,但底层的引擎构造完全不同。今天我们通过 2500 字,彻底拆解这两者的异同,让你从底层理解异步。
1. 核心哲学:谁才是"天生异步"?
JavaScript:停不下来的单线程小伙
JS 从诞生的第一天起,就注定是单线程的。想象一下,如果 JS 是同步的,当你在网页上点击一个按钮,浏览器去下载一张 10MB 的图片,整个网页就会卡死,你连滚动条都拉不动。
因此,JS 必须异步。它的底层是一个永远旋转的事件循环(Event Loop) 。
Python:半路出家的异步教授
Python 本质上是同步的。你写一行代码,它运行一行。为了处理并发,Python 尝试过进程、线程,但受限于 GIL(全局解释器锁) ,多线程在 CPU 密集型任务上表现不佳。
直到 Python 3.4+ 引入 asyncio,Python 才正式开启了异步时代。这意味着在 Python 里,异步是插件式的,你必须显式地启动一个事件循环。
2. 宏观机制:承诺(Promise) vs 协程(Coroutine)
虽然语法长得像,但 await 后面接的东西完全不同。
JS:Promise(承诺)
在 JS 中,异步函数的返回值是一个 Promise。
- 状态驱动:Promise 像一张借条,它有三种状态:进行中(Pending)、成功(Resolved)、失败(Rejected)。
- 立即执行 :当你调用一个
async函数时,它内部的代码会立即开始执行 ,直到遇到第一个await。
Python:Coroutine(协程)
在 Python 中,async def 定义的是一个 Coroutine 对象。
- 惰性求值 :这是最大的区别!当你调用一个 Python 的
async函数时,内部代码一行都不会跑。它只是产生了一个协程对象。 - 必须驱动 :你必须通过
await或者把任务丢进loop(如asyncio.run()),这个协程才会开始呼吸。
比喻:JS 的异步像点外卖,下单后厨师就开始炒菜了;Python 的异步像菜谱,你不生火(启动 Loop),菜永远不会动。
3. 底层驱动:事件循环的权力斗争
JS 的事件循环:宿主(浏览器/Node)说了算
JS 的事件循环是自动开启的,且分为微任务(Microtask)和宏任务(Macrotask) 。
-
Promise.then属于微任务。 -
setTimeout属于宏任务。每次事件循环都会先清空微任务队列。这种机制保证了 JS 的响应速度极快。
Python 的事件循环:用户说了算
Python 的 asyncio 并没有默认开启循环。你需要:
Python
python
import asyncio
async def main():
await asyncio.sleep(1)
# 手动启动循环
asyncio.run(main())
Python 的异步任务被封装为 Task 。它在底层利用了操作系统的 select 或 epoll 机制,让单线程在等待 I/O 时能够切换到另一个任务。
4. 语法实战:await 的爱恨情仇
异常处理
- JS :主要靠
try...catch或者 Promise 的.catch()。 - Python :主要靠标准的
try...except。
并发执行
这是两者最容易写错的地方。
JS 方案:
JavaScript
scss
// 同时启动两个请求
const [res1, res2] = await Promise.all([fetch1(), fetch2()]);
Python 方案:
Python
ini
# 同时启动两个任务
res1, res2 = await asyncio.gather(task1(), task2())
5. 深度差异:传染性与传染范围
"异步传染性"
两者都有传染性:如果你想在一个函数里用 await,这个函数本身必须声明为 async。
JS 的特殊性:Top-level await
在现代 JS 模块中,你可以直接在文件顶层写 await。
JavaScript
csharp
// 直接运行,不需要包在 async 里
const data = await fetch('/api');
Python 的限制:必须有入口
Python 除非在特殊的交互式环境(如 IPython/Jupyter),否则必须有一个 async def main() 入口,并由 asyncio.run() 驱动。
6. 生态挑战:异步 vs 同步库
JS 的纯净性
JS 生态几乎是"全员异步"的。你很难找到一个阻塞主线程的网络库,因为大家都习惯了非阻塞。
Python 的分裂
这是 Python 最尴尬的地方。Python 有大量的同步库(如 requests)。
- 如果你在
async函数里调用了requests.get(),整个事件循环会卡死。 - 你必须寻找异步替代品(如
httpx或aiohttp),或者使用run_in_executor把同步任务丢进线程池。
7. 性能对比:谁更快?
- JS (Node.js) :在处理极高并发的轻量级 I/O(如即时通讯、高频网关)时,V8 引擎的 JIT 优化和成熟的事件循环让它表现极其强悍。
- Python:Python 的异步开销略大,但在逻辑复杂、需要处理科学计算或后台任务的场景下,Python 的语法表达力更强。
8. 总结:该选哪一个?
| 特性 | JavaScript | Python |
|---|---|---|
| 异步性质 | 原生、内置、自动执行 | 库支持、显式、惰性执行 |
| 核心对象 | Promise | Coroutine (协程) |
| 传染性 | 强 (直到顶层 await) | 强 (需要事件循环驱动) |
| 生态库 | 大多默认异步 | 同步异步库并存 (易混用) |
| 适用场景 | 前端交互、高并发 Web 服务 | 爬虫、异步后台处理、AI 接口 |
最终心法:
- 在 JS 里,异步是空气 ,你不需要想它在哪,它无处不在。你要做的是通过
await把乱跳的异步捋平。 - 在 Python 里,异步是手术刀,它是你为了压榨单核性能而特意使用的工具。你必须清晰地知道你的事件循环在哪里开启,哪些库是异步兼容的。