异步:从 Promise 到 asyncio
async/await 几乎同形,事件循环几乎同构 —— 你已有的并发直觉大部分直接平移。
14 分钟 · 右栏代码可点击「运行」
同一个并发抓取
JavaScript
1async function fetchOne(url) {2 const res = await fetch(url);3 return res.status;4}56const status = await Promise.all(7 urls.map(fetchOne)8);
Python
1import asyncio, httpx23async def fetch_one(url):4 async with httpx.AsyncClient() as client:5 r = await client.get(url)6 return r.status_code78status = await asyncio.gather(9 *(fetch_one(u) for u in urls)10)
- 本例需要 httpx 与网络,游乐场跑不了;下一张卡里有一个可运行的纯 asyncio 版本。
Promise.all→asyncio.gather;Promise.race→asyncio.wait(..., return_when=FIRST_COMPLETED)。
可运行的纯 asyncio 对照
JavaScript
1const sleep = ms =>2 new Promise(r => setTimeout(r, ms));34async function task(name, s) {5 await sleep(s * 1000);6 return name + " done";7}89const out = await Promise.all([10 task("a", 1), task("b", 2),11]);
Python
1import asyncio23async def task(name, s):4 await asyncio.sleep(s)5 return name + " done"67async def main():8 out = await asyncio.gather(9 task("a", 1), task("b", 2)10 )11 print(out)1213asyncio.run(main()) # 显式入口,JS 没有这一步
- 语法差异只有两处:
async function→async def,以及顶层 await 在脚本里不合法,需要asyncio.run(main())包一层(Jupyter 里顶层 await 直接可用)。
注意
两个 JS 没有的坑 — ① 忘了 await 的协程不会执行,只会得到一条警告 —— 比 unhandled rejection 更安静。② 事件循环里任何同步阻塞调用(time.sleep、大计算、同步 IO)会冻住一切,和 JS 相同但没有微任务宏任务的缓冲说法,别混用同步库。
练一练
这张卡的示例代码已在游乐场中备好,改一改再运行。