我需要创建一个Observable流,该流以规则的时间间隔发出异步协程的结果。
intervalRead
是一个返回Observable的函数,并将间隔rate
和异步协程函数fun
作为参数,需要在定义的间隔处调用。
我的第一个方法是使用interval工厂方法创建一个observable,然后使用map调用协程,使用from_future将其包装到Observable中,然后获取协程返回的值。
async def foo():
await asyncio.sleep(1)
return 42
def intervalRead(rate, fun) -> Observable:
loop = asyncio.get_event_loop()
return rx.interval(rate).pipe(
map(lambda i: rx.from_future(loop.create_task(fun()))),
)
async def main():
obs = intervalRead(5, foo)
obs.subscribe(
on_next= lambda item: print(item)
)
loop = asyncio.get_event_loop()
loop.create_task(main())
loop.run_forever()
但是我得到的输出不是协程的结果,而是from_future返回的Observable,它以指定的时间间隔发出
输出:<rx.core.observable.observable.Observable object at 0x033B5650>
如何获取该Observable返回的实际值?我希望42
第二个方法是创建一个自定义的可观察对象:
def intervalRead(rate, fun) -> rx.Observable:
interval = rx.interval(rate)
def subs(observer: Observer, scheduler = None):
loop = asyncio.get_event_loop()
def on_timer(i):
task = loop.create_task(fun())
from_future(task).subscribe(
on_next= lambda i: observer.on_next(i),
on_error= lambda e: observer.on_error(e),
on_completed= lambda: print('coro completed')
)
interval.subscribe(on_next= on_timer, on_error= lambda e: print(e))
return rx.create(subs)
但是,在订阅from_future(task)
上从未发出任何值,为什么会发生这种情况?
但是如果我这样写intervalRead
:
def intervalRead(rate, fun):
loop = asyncio.get_event_loop()
task = loop.create_task(fun())
return from_future(task)
我得到了预期的结果:42
。显然,这不能解决我的问题,但是使我感到困惑,为什么我的第二种方法不起作用?
最后,我使用rx.concurrency CurrentThreadScheduler
进行了第三次尝试,并使用schedule_periodic
方法定期安排了一项操作。但是我也面临着第二种方法遇到的相同问题。
def funWithScheduler(rate, fun):
loop = asyncio.get_event_loop()
scheduler = CurrentThreadScheduler()
subject = rx.subjects.Subject()
def action(param):
obs = rx.from_future(loop.create_task(fun())).subscribe(
on_next= lambda item: subject.on_next(item),
on_error= lambda e: print(f'error in action {e}'),
on_completed= lambda: print('action completed')
)
obs.dispose()
scheduler.schedule_periodic(rate,action)
return subject
将感谢您对我所缺少的任何见解或完成我需要的任何其他建议。这是我的第一个使用asyncio和RxPY的项目,我只在有角项目的上下文中使用RxJS,因此欢迎任何帮助。
答案 0 :(得分:0)
您的第一个示例几乎可行。要使其正常运行,只需进行两项更改:
首先from_future的结果是一个可观察的对象,它发出单个项目(完成后的Future值)。因此,map的输出是一个更高阶的可观察值(发出可观察值的可观察值)。可以通过在地图之后使用merge_all运算符或通过使用flat_map而不是map来展平这些孩子的可观察对象。
然后,时间间隔运算符必须在AsyncIO循环上调度其计时器,默认情况下不是这样:默认调度程序是TimeoutScheduler,它产生一个新线程。因此,在原始代码中,无法在AsyncIO事件循环上安排任务,因为create_task是从另一个线程调用的。在呼叫订阅上使用scheduler参数声明用于整个操作员链的默认调度程序。
以下代码有效(每5秒打印一次42):
import asyncio
import rx
import rx.operators as ops
from rx.scheduler.eventloop import AsyncIOScheduler
async def foo():
await asyncio.sleep(1)
return 42
def intervalRead(rate, fun) -> rx.Observable:
loop = asyncio.get_event_loop()
return rx.interval(rate).pipe(
ops.map(lambda i: rx.from_future(loop.create_task(fun()))),
ops.merge_all()
)
async def main(loop):
obs = intervalRead(5, foo)
obs.subscribe(
on_next=lambda item: print(item),
scheduler=AsyncIOScheduler(loop)
)
loop = asyncio.get_event_loop()
loop.create_task(main(loop))
loop.run_forever()