【问题标题】:Python call callback after async function is done异步函数完成后的 Python 调用回调
【发布时间】:2019-03-05 13:17:06
【问题描述】:

我正在尝试在异步函数运行完成后调用回调

这是我尝试做的一个例子:

import asyncio

async def asyncfunction():
    print('Hello')
    await asyncio.sleep(10)
    print('World')
    return 10

def callback(n):
    print(f'The async function returned: {n}')

loop = asyncio.get_event_loop()

# Will block the print until everything is done
callback(loop.run_until_complete(asyncfunction()))
print('Hey')

这是做什么的:

Hello
World
The async function returned: 10
Hey

这就是我想要它做的事情
编辑:“嘿”的位置并不重要,只要它不必等待异步功能完成

Hello
Hey
World
The async function returned: 10

编辑:经过一些测试,我找到了一种可以满足我需求的方法,尽管我不知道它是否是最好的方法

import asyncio
import threading

async def asyncfunction():
    print('Hello')
    await asyncio.sleep(10)
    print('World')
    return 10

def callback(n):
    print(f'The async function returned: {n}')

def wrapper(loop):
    callback(loop.run_until_complete(asyncfunction()))

loop = asyncio.get_event_loop()
thr = threading.Thread(target=wrapper,args=(loop,))
thr.start()
print('Hey')

【问题讨论】:

  • print('Hello') 之后移动print('Hey')?我知道这是蛮力,但你还没有谈到你想要的输出的逻辑。
  • @mat,为什么你认为你的回调是回调?经典的回调应该作为参数传递给函数,但你的代码完全不同。

标签: python python-asyncio


【解决方案1】:

将线程与 asyncio 一起使用只会令人困惑,而且很可能不是您想要的。 run_until_complete 是阻塞调用之一,应该是asyncio 程序中的最后一条语句。

要在调用异步函数后添加代码,只需创建一个包装器

async def myfunc():
  n = await asyncfunction()
  callback(n)

loop.run_until_complete(myfunc()) # from python 3.7, asyncio.run(myfunc())

如果您只想安排一些代码异步运行并继续执行其他操作,请创建一个任务并在最后等待

async def a_main():
  task = asyncio.ensure_future(myfunc()) # from python 3.7, asyncio.create_task(...)
  print("Hey")
  # Anything else to run
  await task # wait for the task to complete

loop.run_until_complete(a_main())

【讨论】:

    【解决方案2】:

    免责声明:Following code creates different threads for each function.

    这可能对某些情况有用,因为它更易于使用。但是要知道它不是异步的,而是使用多个线程产生异步的错觉,尽管装饰器建议这样做。

    要使任何函数非阻塞,只需复制装饰器并使用回调函数作为参数装饰任何函数。回调函数会接收函数返回的数据。

    import asyncio
    import requests
    
    
    def run_async(callback):
        def inner(func):
            def wrapper(*args, **kwargs):
                def __exec():
                    out = func(*args, **kwargs)
                    callback(out)
                    return out
    
                return asyncio.get_event_loop().run_in_executor(None, __exec)
    
            return wrapper
    
        return inner
    
    
    def _callback(*args):
        print(args)
    
    
    # Must provide a callback function, callback func will be executed after the func completes execution !!
    @run_async(_callback)
    def get(url):
        return requests.get(url)
    
    
    get("https://google.com")
    print("Non blocking code ran !!")
    

    【讨论】:

    • 很高兴看到您发布答案!我建议添加一些有关您修复的内容或正确原因的详细信息。它可以帮助用户了解正在发生的事情,而不仅仅是提供一个大代码块作为答案。将其视为您所有答案的规则。我希望你玩得开心。
    • @Aman 谢谢哥们.. 我会尽力详细解释代码:)
    【解决方案3】:

    要获得该顺序,您需要在print('Hey') 之后继续执行协程。您还需要在asyncfunction 处于睡眠状态时将'Hey' 打印在“平静”中。这基本上只能由事件循环本身安排;因为asyncfunction 是一个你所知道的黑盒子,你不知道它在等待什么,为什么或者可以在它睡觉时明确地从它那里获得控制权。

    因此,将asyncfunctionprint('Hey') 作为异步任务执行,并且主要希望调度能够成功,以便在asyncfunction 休眠时安排“嘿”运行。

    val, *_ = loop.run_until_complete(asyncio.gather(
        asyncfunction(),
        asyncio.coroutine(print)('Hey')
    ))
    callback(val)
    

    asyncio.coroutine(print)print 转换为async 函数,并且gather 同时在事件循环上调度,它很可能 会发现print 将在同时执行asyncfunction 正在睡觉。

    【讨论】:

      【解决方案4】:

      这是我的解决方案,以及名为run_with_callback工具函数

      # problem
      
      async def asyncfunction(n):
          print(f'before sleep in asyncfunction({ n })')
          await asyncio.sleep(1)
          print(f'after sleep in asyncfunction({ n })')
          return f'result of asyncfunction({ n })'
      
      def callback(r):
          print(f'inside callback, got: {r}')
      
      
      # straightforward solution
      
      import asyncio
      
      async def wrapper(n):
          r = await asyncfunction(n)
          callback(r)
      
      asyncio.get_event_loop().create_task(wrapper(1))
      print('sync code following loop.create_task(1)')
      
      asyncio.get_event_loop().create_task(wrapper(2))
      print('sync code following loop.create_task(2)')
      
      
      # general solution with a tool function
      
      def run_with_callback(co):
          def wrapper(callback):
              task = asyncio.get_event_loop().create_task(co)
              task.add_done_callback(lambda t: callback(t.result()))
              return callback
          return wrapper
      
      # use as function
      
      run_with_callback(asyncfunction(3))(callback)
      print('sync code following loop.create_task(3)')
      
      # use as decorator
      
      @run_with_callback(asyncfunction(4))
      def _tmp_callback_1(r):
          callback(r)
      print('sync code following loop.create_task(4)')
      
      # main
      
      _all = asyncio.gather(*asyncio.all_tasks(asyncio.get_event_loop()))
      asyncio.get_event_loop().run_until_complete(_all)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-09-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-24
        • 1970-01-01
        • 2014-05-12
        相关资源
        最近更新 更多