86

I was trying the following code:

import asyncio

@asyncio.coroutine
def func_normal():
        print("A")
        yield from asyncio.sleep(5)
        print("B")
        return 'saad'

@asyncio.coroutine
def func_infinite():
    i = 0
    while i<10:
        print("--"+str(i))
        i = i+1
    return('saad2')

loop = asyncio.get_event_loop()

tasks = [
    asyncio.async(func_normal()),
    asyncio.async(func_infinite())]

loop.run_until_complete(asyncio.wait(tasks))
loop.close()

I can't figure out how to get values in variables from these functions. I can't do this:

asyncio.async(a = func_infinite())

as this would make this a keyword argument. How do I go about accomplishing this?

4 Answers 4

87

The coroutines work as is. Just use the returned value from loop.run_until_complete() and call asyncio.gather() to collect multiple results:

#!/usr/bin/env python3
import asyncio

@asyncio.coroutine
def func_normal():
    print('A')
    yield from asyncio.sleep(5)
    print('B')
    return 'saad'

@asyncio.coroutine
def func_infinite():
    for i in range(10):
        print("--%d" % i)
    return 'saad2'

loop = asyncio.get_event_loop()
tasks = func_normal(), func_infinite()
a, b = loop.run_until_complete(asyncio.gather(*tasks))
print("func_normal()={a}, func_infinite()={b}".format(**vars()))
loop.close()

Output

--0
--1
--2
--3
--4
--5
--6
--7
--8
--9
A
B
func_normal()=saad, func_infinite()=saad2
Sign up to request clarification or add additional context in comments.

4 Comments

This works. I did run functions for my django app concurrently using this approach but I didn't see a gain in performance. It takes the same time to process.
@AleemSaadullah: asyncio allows you to run 10 func_normal() tasks in 5 seconds instead of 50 seconds. All tasks are executed in the same thread unless you use an executor to offload some work to a thread/process pool. I'm not familiar with running django using asyncio (if it is even possible or whether it makes any sense without substancial restructuring in django).
Yes, the code works for functions such as the ones defined here. But I guess, the mongodb ORM(mongoengine) is supposed to support asynchronous queries. I think that is the problem here.
The functions in my actual project primarily build queries and execute them.
17

loop.run_until_complete returns the value returned by the function you pass into it. So, it will return the output of asyncio.wait:

import asyncio

@asyncio.coroutine
def func_normal():
    print("A")
    yield from asyncio.sleep(5)
    print("B")
    return 'saad'

@asyncio.coroutine
def func_infinite():
    i = 0
    while i<10:
        print("--"+str(i))
        i = i+1
    return('saad2')

loop = asyncio.get_event_loop()

tasks = [
    asyncio.async(func_normal()),
    asyncio.async(func_infinite())]

done, _ = loop.run_until_complete(asyncio.wait(tasks))
for fut in done:
    print("return value is {}".format(fut.result()))
loop.close()

Output:

A
--0
--1
--2
--3
--4
--5
--6
--7
--8
--9
B
return value is saad2
return value is saad

You can also access the results directly from the tasks array:

print(tasks[0].result())
print(tasks[1].result())

Comments

5

If you want to use any value returned by coroutine as soon as coroutine ends you can pass future object into the coro and update this future by computed value. As soon as future is updated it passes its future.result() to the callback function which is bound with given future. See code below:

import asyncio


async def func_normal(future):
    print("A")
    await asyncio.sleep(5)
    print("B")
    # return 'saad'
    future.set_result('saad')


async def func_infinite(future):
    i = 0
    while i<10:
        print("--"+str(i))
        i = i+1
    # return('saad2')
    future.set_result('saad2')

def got_result(future):
    print(future.result())

loop = asyncio.get_event_loop()
future1 = asyncio.Future()
future2 = asyncio.Future()

future1.add_done_callback(got_result)
future2.add_done_callback(got_result)

# Coros are automatically wrapped in Tasks by asyncio.wait() 
coros = [
    func_normal(future1),
    func_infinite(future2)]

loop.run_until_complete(asyncio.wait(coros))
loop.close()

The callback function is called with a single argument - the future object which it is bound with. If you need to pass more arguments into the callback use partial from functools package:

future1.add_done_callback(functools.partial(print, "future:", argin))

will call

print("future:", argin)

Comments

3

I found a solution that I like better:

    loop = asyncio.new_event_loop()
    task = loop.create_task(awaitable(*args, **kwargs))

    loop.run_until_complete(task)

    return task.result()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.