Python 和 Node.js 的异步并发实现机制有显著不同,尽管它们都提供了 async/await 语法。

Python 的异步并发实现

Python 的异步并发基于事件循环(Event Loop)协程(Coroutine)

核心组件:

  1. 事件循环(Event Loop):作为中央执行器,负责调度和执行协程,并处理 IO 事件。
  2. 协程(Coroutine):使用 async def 定义的函数,返回一个协程对象。协程可以在事件循环中挂起和恢复。
  3. 任务(Task):对协程的进一步封装,表示一个可调度的单元。

工作流程:

  • 事件循环管理一个任务队列,循环执行就绪的任务。
  • 当任务遇到 IO 操作时,它会挂起自己(通过 await),并将控制权交还给事件循环。
  • 事件循环会监视 IO 操作(通过 selector 模块),当 IO 完成时,将对应的任务标记为就绪,等待下一次循环执行。

示例代码:

import asyncio
 
async def fetch_data():
    print(" 开始获取数据 ")
    await asyncio.sleep(2)  # 模拟 IO 操作,挂起当前协程
    print(" 数据获取完成 ")
    return " 数据 "
 
async def main():
 
# 创建多个任务,这些任务在事件循环中并发执行
 
    task1 = asyncio.create_task(fetch_data())
    task2 = asyncio.create_task(fetch_data())
    
    # 等待两个任务完成
    await task1
    await task2
 
asyncio.run(main())

并发模型:

  • 任务(Task):Python 的 asyncio 通常是单线程的,通过事件循环在单个线程中实现并发。
  • 单线程:任务通过 await 主动让出控制权,实现协作式多任务。

Node.js 的异步并发实现

Node.js 的异步并发基于事件循环(Event Loop)回调函数,后来引入了 Promise 和 async/await。

核心组件:

  1. 事件循环(Event Loop):由 libuv 库提供,负责执行延迟任务和 IO 事件回调。
  2. 回调函数(Callback):异步操作完成后执行的函数。
  3. Promise:表示异步操作最终完成或失败的对象。
  4. async/await:基于 Promise 的语法糖,使异步代码看起来像同步代码。

工作流程:

  • Node.js 启动时,初始化事件循环,然后执行主线程代码。
  • 遇到异步操作(如文件读写、网络请求),将其交给底层线程池(由 libuv 管理)执行,主线程继续执行后续代码。
  • 异步操作完成后,将回调函数放入事件循环的任务队列。
  • 事件循环在适当的时候(主线程空闲时)从任务队列中取出回调函数并执行。

示例代码:

async function fetchData() {
    console.log(" 开始获取数据 ");
    // 模拟 IO 操作,返回一个 Promise
    await new Promise(resolve => setTimeout(resolve, 2000));
    console.log(" 数据获取完成 ");
    return " 数据 ";
}
 
async function main() {
    // 创建多个异步任务,它们会并发执行
    const task1 = fetchData();
    const task2 = fetchData();
 
    // 等待两个任务完成
    await task1;
    await task2;
}
 
main();

并发模型:

  • 单线程 + 线程池:Node.js 的主线程是单线程,但 IO 操作由底层线程池(libuv)执行,从而实现非阻塞 IO。
  • 事件驱动:通过事件循环处理 IO 事件回调,实现高并发。

两者对比

特性Python asyncioNode.js
底层实现asyncio(内置)libuv(C 语言编写)
并发模型单线程(协程)单线程(主线程)+ 线程池(IO 操作)
调度方式协作式多任务(任务主动让出)事件驱动(回调函数)
IO 支持只能异步化支持 asyncio 的库(如 aiohttp)几乎所有 IO 操作都是异步的
CPU 密集型任务会阻塞事件循环(需要多进程处理)会阻塞主线程(需要子进程或 Worker Threads)
生态系统异步库逐渐丰富从开始就围绕异步构建

注意事项

Python:

  • 异步代码必须在异步函数中运行(使用 await)。
  • 标准库中很多模块不支持异步,需要使用异步版本(如 aiohttp 代替 requests)。
  • CPU 密集型任务会阻塞事件循环,需要使用多进程(如 asyncio.run_in_executor)。

Node.js:

  • 几乎所有 IO 操作都是异步的,但 CPU 密集型操作会阻塞主线程。
  • 可以使用 Worker Threads 来处理 CPU 密集型任务。
  • 错误处理需要小心,未捕获的 Promise 拒绝可能会导致进程退出。

总结

两者都利用事件循环实现异步并发,但实现细节和生态系统有较大差异。Python 的 asyncio 更侧重于单线程内的协程并发,而 Node.js 则从设计之初就围绕异步 IO 构建,拥有更成熟的异步生态系统。