Эквивалент promisify
не будет работать для этого варианта использования по двум причинам:
- Асинхронный API PyAudio не использует цикл событий asyncio - в документации указано, что обратный вызов вызываетсяиз фоновой темы.Это требует мер предосторожности для правильной связи с asyncio.
- Обратный вызов не может быть смоделирован одним будущим, поскольку он вызывается несколько раз, тогда как будущее может иметь только один результат.Вместо этого он должен быть преобразован в асинхронный итератор, как показано в примере кода.
Вот одна из возможных реализаций:
async def make_iter():
loop = asyncio.get_event_loop()
queue = asyncio.Queue()
def put(*args):
loop.call_soon_threadsafe(queue.put_nowait, args)
async def get():
while True:
yield queue.get()
return get(), put
make_iter
возвращает пара из,Возвращенные объекты содержат свойство, которое вызывает обратный вызов, заставляет итератор выдавать свое следующее значение (аргументы, переданные обратному вызову).Обратный вызов может быть вызван для вызова из произвольного потока, и поэтому его можно безопасно передать на pyaudio.open
, в то время как асинхронный итератор должен быть передан async for
в сопрограмме asyncio, которая будет приостановлена при ожидании следующего значения:
async def main():
stream_get, stream_put = make_iter()
stream = pa.open(stream_callback=stream_put)
stream.start_stream()
async for in_data, frame_count, time_info, status in stream_get:
# ...
asyncio.get_event_loop().run_until_complete(main())
Обратите внимание, что согласно документации обратный вызов также должен возвращать значащее значение, кортеж фреймов и логический флаг.Это может быть включено в проект путем изменения функции fill
, чтобы также получать данные со стороны асинхронного режима.Реализация не включена, потому что она может не иметь большого смысла без понимания предметной области.