node.js事件循环由libuv实现,按timers→pending callbacks→poll→check等阶段调度宏任务;python asyncio默认用selectoreventloop,协程需显式create_task提交,await抛异常可被try/except直接捕获。

Node.js 的 event loop 是怎么调度异步任务的
Node.js 的异步模型核心是 libuv 提供的事件循环,它把所有异步 IO(如 fs.readFile、http.request)委托给底层线程池或操作系统事件机制(epoll/kqueue/IOCP),主线程不阻塞。事件完成时,回调被推入「宏任务队列」,由 event loop 按阶段(timers → pending callbacks → idle/prepare → poll → check → close callbacks)依次取出执行。
关键点在于:所有用户代码都在单线程中串行执行,不存在竞态;但回调顺序受阶段约束,setTimeout(fn, 0) 不一定比 setImmediate(fn) 先执行——这和直觉相反,是容易踩的坑。
Python 的 asyncio event loop 依赖什么运行时
Python 的 asyncio 默认使用 SelectorEventLoop(基于 select/poll/epoll),也可切换为 ProactorEventLoop(Windows 上用 IOCP)。它不自带线程池,所有异步 IO 都需显式通过 loop.run_in_executor() 交由线程池处理阻塞操作(比如 time.sleep() 或同步库调用)。
常见误区是以为 await asyncio.sleep(1) 和 time.sleep(1) 等价——前者让出控制权,后者直接卡死整个 event loop。协程调度靠 asyncio.create_task() 显式提交,不像 Node.js 那样自动注册回调。
为什么 aiohttp 并发请求比 requests + threading 更稳
requests 是同步库,即使丢进 ThreadPoolExecutor,每个线程仍独占一个 TCP 连接、独立解析响应,连接复用难、内存开销大;而 aiohttp 复用底层 asyncio 的连接池,所有协程共享 session,DNS 缓存、TCP Keep-Alive、HTTP/2 流复用都由库自动管理。
实操建议:
- 避免在
async函数里直接调用requests.get()——会阻塞整个 loop - 用
aiohttp.ClientSession而非反复新建ClientSession() - 超时必须设
timeout=参数,否则 DNS 卡住时协程永不返回
Python 协程与 Node.js 回调在错误传播上有什么区别
Node.js 的回调函数通常遵循 callback(err, result) 模式,错误必须手动检查,漏判就会静默失败;Promise 链中未加 .catch() 会导致 unhandled rejection,但默认不崩溃进程。
Python 的 await 表达式会直接抛出异常,可以被外层 try/except 捕获,语义更接近同步代码。但要注意:asyncio.gather() 默认遇到第一个异常就中断,需加 return_exceptions=True 才能收集全部结果。
真正容易被忽略的是:Node.js 的 process.on('unhandledRejection') 和 Python 的 asyncio.get_event_loop().set_exception_handler() 都得显式设置,否则异常会丢失——线上爬虫跑着跑着没日志也没报错,多半是这里漏了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











