0

I'm trying to wrap an async function up so that I can use it without importing asyncio in certain files. The ultimate goal is to use asynchronous functions but being able to call them normally and get back the result.

How can I access the result from the callback function printing(task) and use it as the return of my make_task(x) function?

MWE:

#!/usr/bin/env python3.7

import asyncio

loop = asyncio.get_event_loop()

def make_task(x):   # Can be used without asyncio
    task = loop.create_task(my_async(x))
    task.add_done_callback(printing)
    # return to get the 

def printing(task):
    print('sleep done: %s' % task.done())
    print('results: %s' % task.result())
    return task.result()    # How can i access this return?

async def my_async(x):  # Handeling the actual async running
    print('Starting my async')
    res = await my_sleep(x)
    return res  # The value I want to ultimately use in the real callback

async def my_sleep(x):
    print('starting sleep for %d' % x)
    await asyncio.sleep(x)
    return x**2

async def my_coro(*coro):
    return await asyncio.gather(*coro)

val1 = make_task(4)
val2 = make_task(5)
loop.run_until_complete(my_coro(asyncio.sleep(6)))

print(val1)
print(val2)
Kajsa
  • 211
  • 1
  • 16

2 Answers2

1

If I understand correctly you want to use asynchronous functions but don't want to write async/await in top-level code.

If that's the case, I'm afraid it's not possible to achieve with asyncio. asyncio wants you to write async/await everywhere asynchronous stuff happens and this is intentional: forcing to explicitly mark places of possible context switch is a asyncio's way to fight concurrency-related problems (which is very hard to fight otherwise). Read this answer for more info.

If you still want to have asynchronous stuff and use it "as usual code" take a look at alternative solutions like gevent.

Mikhail Gerasimov
  • 36,989
  • 16
  • 116
  • 159
0

Instead of using a callback, you can make printing a coroutine and await the original coroutine, such as my_async. make_task can then create a task out of printing(my_async(...)), which will make the return value of printing available as the task result. In other words, to return a value out of printing, just - return it.

For example, if you define make_task and printing like this and leave the rest of the program unchanged:

def make_task(x):
    task = loop.create_task(printing(my_async(x)))
    return task

async def printing(coro):
    coro_result = await coro
    print('sleep done')
    print('results: %s' % coro_result)
    return coro_result

The resulting output is:

Starting my async
starting sleep for 4
Starting my async
starting sleep for 5
sleep done
results: 16
sleep done
results: 25
<Task finished coro=<printing() done, defined at result1.py:11> result=16>
<Task finished coro=<printing() done, defined at result1.py:11> result=25>
user4815162342
  • 141,790
  • 18
  • 296
  • 355
  • Yes, but the problem is the return from `make_task`. The only thing I can get to return is the task object, I want to return the value `x**2`. – Kajsa Aug 30 '19 at 08:43
  • @Kajsa That's not obvious from the question because your code at top-level calls `make_task` **twice**, which makes it seem like you really want to just make the task at that point (and not wait for it to complete), and retrieve the values later. So, which is it - would you like `make_task` to block the current thread until the task is done, or would you like it to just submit the task to an event loop? – user4815162342 Aug 30 '19 at 11:09
  • I clearly state in the question that I want to "access the result from the callback function printing(task) and use it as the return of my make_task(x) function". So what I want is for the task to be added to the loop and not block the rest of the async code but from the outside if the module calling `make_task` looks like a normal method. I understand that this is an unconventional thing to try to do and that it might not be possible at all. I though I'd still ask since there might be a work around that makes it seem like the call returns the intended value. – Kajsa Aug 30 '19 at 13:45
  • @Kajsa So, you want `make_task` to block the current thread until a result is available? – user4815162342 Aug 30 '19 at 15:27