功能说明
bounded_map 用 asyncio.Semaphore 把同时在执行的协程数量限制在 limit 以内。每个输入先包装成 guarded 协程,拿到信号量后才调用真正的 worker;asyncio.gather(..., return_exceptions=True) 会等待全部任务结束,因此单个任务失败不会取消其他任务。返回值是 Outcome 列表,成功项带 value,失败项带 error,调用方可以按自己的策略决定跳过、重试还是整体失败。
关键代码
信号量只包围真正的等待段,而不是结果处理:
gate = asyncio.Semaphore(limit)
async def guarded(item: T) -> R:
async with gate:
return await worker(item)
outcomes = await asyncio.gather(*(guarded(item) for item in items), return_exceptions=True)
return_exceptions=True 会把任务抛出的异常变成列表元素;如果任务被取消,仍应让取消向上传播。
使用注意
limit必须大于 0,函数入口已经做了校验,避免Semaphore构造时报错。- 结果顺序与输入顺序一致,因为
gather按传入顺序返回,而不是按完成顺序。 - 该实现会一次性创建所有协程对象;条目数量极大时应分批提交,避免提前占用大量内存。
- 单个任务失败不会停止其他任务,但会等待所有任务结束才返回,适合可以容忍部分失败的批处理。
- 如果需要快速失败,可以在第一个错误出现时取消其余任务,并捕获
CancelledError做清理。
验证方式
直接运行示例:编号 4 的请求会抛出 RuntimeError,输出中应有 6 条成功记录和 1 条失败记录。也可以把 limit 改成 1 或 8 观察总耗时变化;再临时去掉 return_exceptions=True 做对比,确认异常会立刻打断 gather。