在Pyodide/Stlite中处理API请求:同步等待协程完成方案
在Pyodide中同步等待异步pyfetch完成以初始化类数据
问题核心
你遇到的TypeError: coroutine object is not subscriptable错误,本质是因为:
- 标记为
async的get_data方法直接调用时,返回的是协程对象而非实际的DataFrame,所以无法用[]下标访问; - 你修改后的
get_data里漏了关键一步:response.json()本身也是异步方法,必须用await等待解析完成。
解决方案
以下两种方案可解决同步等待异步请求的问题,根据你的场景选择:
方案一:异步工厂方法(推荐,符合异步编程规范)
将类的初始化逻辑改为异步,通过工厂方法创建实例,避免在__init__中直接处理异步操作:
import asyncio import pandas as pd from pyodide.http import pyfetch from typing import Final class FabmanData: LINKS: Final = { "members": "members?orderBy=name&order=asc", "resource": "resources?orderBy=name&order=asc", "bookings": "bookings?order=desc&limit=50&summary=false", } def __init__(self) -> None: # 先初始化空属性,后续异步填充 self.members: pd.DataFrame = pd.DataFrame() self.resources: pd.DataFrame = pd.DataFrame() self.latest_bookings: pd.DataFrame = pd.DataFrame() async def init_data(self) -> None: # 异步获取并处理所有数据 self.members = (await self.get_data("members"))[ ["id", "firstName", "lastName", "memberNumber"] ] self.resources = (await self.get_data("resource"))[ ["id", "name", "state"] ] self.latest_bookings = (await self.get_data("bookings"))[ ["id", "resource", "fromDateTime", "untilDateTime", "member"] ] @staticmethod async def get_data(category) -> pd.DataFrame: url = f"{BASEURL}{FabmanData.LINKS[category]}" # 给请求加超时控制(正确用法) response = await asyncio.wait_for( pyfetch(url=url, headers=HEADERS), timeout=10 ) # 等待JSON解析完成 data = await response.json() return pd.DataFrame(data) @classmethod async def create(cls): # 异步工厂方法,创建并初始化实例 instance = cls() await instance.init_data() return instance def get_resources_dict(self): return { resource: resource_id for resource, resource_id in zip( self.resources["name"], self.resources["id"] ) }
使用方式:
# 在异步上下文(比如Streamlit的async回调、Pyodide的主异步函数)中创建实例 fabman_data = await FabmanData.create()
方案二:同步上下文用asyncio.run阻塞等待
如果必须在同步的__init__中完成初始化,可以用asyncio.run直接运行协程并阻塞等待结果:
import asyncio import pandas as pd from pyodide.http import pyfetch from typing import Final class FabmanData: LINKS: Final = { "members": "members?orderBy=name&order=asc", "resource": "resources?orderBy=name&order=asc", "bookings": "bookings?order=desc&limit=50&summary=false", } def __init__(self) -> None: # 用asyncio.run阻塞等待协程完成,直接拿到DataFrame self.members: pd.DataFrame = asyncio.run(self.get_data("members"))[ ["id", "firstName", "lastName", "memberNumber"] ] self.resources: pd.DataFrame = asyncio.run(self.get_data("resource"))[ ["id", "name", "state"] ] self.latest_bookings: pd.DataFrame = asyncio.run(self.get_data("bookings"))[ ["id", "resource", "fromDateTime", "untilDateTime", "member"] ] @staticmethod async def get_data(category) -> pd.DataFrame: url = f"{BASEURL}{FabmanData.LINKS[category]}" response = await asyncio.wait_for( pyfetch(url=url, headers=HEADERS), timeout=10 ) data = await response.json() return pd.DataFrame(data) def get_resources_dict(self): return { resource: resource_id for resource, resource_id in zip( self.resources["name"], self.resources["id"] ) }
关键注意点
- 原代码中
await asyncio.wait_for(response, timeout=10)是错误用法,wait_for需要包裹pyfetch调用,才能给整个请求加超时限制; - 确保
BASEURL和HEADERS已正确定义(比如HEADERS是包含认证信息的字典,如{"Authorization": "Bearer YOUR_TOKEN"})。
内容的提问来源于stack exchange,提问作者pj43
相关产品推荐
相关产品推荐

