如何在for循环中异步发起API请求?最简asyncio实现及问题求助
解决API请求并发执行并等待全部完成的问题
你原来代码的问题
你之前的写法有两个关键错误:
loop.run_in_executor(executor, get_data(ticker))会立即同步执行get_data(ticker),而不是把任务提交给执行器异步处理。正确写法应该是传递函数对象和参数:loop.run_in_executor(executor, get_data, ticker)。loop.run_forever()会让事件循环一直运行无法主动结束,应该用asyncio.gather等待所有任务完成后自动退出。
推荐实现方案
API请求属于IO密集型场景,优先选择asyncio + ThreadPoolExecutor(兼容同步函数),或者直接用异步HTTP库实现纯异步请求。
方案1:asyncio + ThreadPoolExecutor(兼容同步get_data函数)
如果你的get_data是同步函数(比如基于requests库),用线程池并发执行避免阻塞事件循环:
import asyncio from concurrent.futures import ThreadPoolExecutor import pandas as pd # 你的同步get_data函数 def get_data(ticker): # 替换为实际API请求逻辑 import time time.sleep(1) # 模拟网络延迟 return pd.DataFrame({'ticker': [ticker], 'close': [150 + int(ticker[-1])]}) async def main(tickers): # 线程池大小根据API并发限制调整,比如5-10 with ThreadPoolExecutor(max_workers=5) as executor: loop = asyncio.get_running_loop() # 批量提交所有任务,收集future对象 tasks = [loop.run_in_executor(executor, get_data, ticker) for ticker in tickers] # 等待所有任务完成,获取所有DataFrame结果 results = await asyncio.gather(*tasks) # 合并结果或单独处理每个DataFrame combined_df = pd.concat(results, ignore_index=True) print(combined_df) if __name__ == "__main__": tickers = ['AAPL', 'MSFT', 'GOOGL', 'TSLA'] asyncio.run(main(tickers))
方案2:纯异步实现(推荐,需改造get_data)
如果可以用异步HTTP库(比如aiohttp)重写get_data,性能更优且无需线程池:
import asyncio import aiohttp import pandas as pd async def get_data(ticker): # 用aiohttp发起异步API请求 async with aiohttp.ClientSession() as session: # 替换为实际API地址 api_url = f"https://api.your-finance-service.com/{ticker}" async with session.get(api_url) as resp: data = await resp.json() # 转换为DataFrame返回 return pd.DataFrame(data) async def main(tickers): # 创建所有异步任务 tasks = [get_data(ticker) for ticker in tickers] # 等待所有任务完成,拿到结果列表 results = await asyncio.gather(*tasks) # 合并结果示例 combined_df = pd.concat(results, ignore_index=True) print(combined_df) if __name__ == "__main__": tickers = ['AAPL', 'MSFT', 'GOOGL', 'TSLA'] asyncio.run(main(tickers))
关键说明
asyncio.gather(*tasks):同时运行所有传入的异步任务,等待全部完成后返回结果列表,顺序与任务传入顺序一致。- 线程池/进程池选择:IO密集型任务用线程池足够;CPU密集型任务(如复杂计算)才考虑ProcessPoolExecutor。
- 并发限制:不要设置过大的线程池/任务数,避免触发API限流,建议参考API文档调整
max_workers。
内容的提问来源于stack exchange,提问作者alexx0186
相关产品推荐
相关产品推荐

