calling a function asynchronously

Kirill Ratkin kirill.ratkin at devoteam.com
Wed Mar 30 15:32:11 EDT 2022


Hi

30.03.2022 21:44, Larry Martell пишет:
> On Wed, Mar 30, 2022 at 2:40 PM Kirill Ratkin via Python-list
> <python-list at python.org>  wrote:
>> Hi again,
>>
>> I changed a bit your example and it works as you expected I hope.
>>
>> import asyncio
>>
>>
>> async def long():
>>       for i in range(100):
>>           await asyncio.sleep(10)
>>           print("long is done")
>>
>>
>> loop = asyncio.get_event_loop()
>>
>> task = loop.create_task(long())
>> print('after asyncio.run')
>> loop.run_until_complete(asyncio.gather(task))
>>
>>
>> But how I wrote before ... if you are in big Django project just look at
>> existent django libraries for long task running. One of it I sent you.
>> There is 'celery' as well.
>>
>> It's more pragmatic way ...
> Appreciate the reply. I did not know about django-background-tasks -
> thanks. I've been trying to make use of that. I do not get the errors
> I was getting before but it does not appear that my long running task
> is running at all. Still debugging. But concerting asyncio - doesn't
> run_until_complete block until long() completes?

Yes, It runs until the /future/ has completed.

In example above 'gather' is not necessary because you create one async 
task only.

You can pass 'task' to run_until_complete directly.

But if you create several tasks, all of it need to be run concurently 
and 'gather' does it.

import asyncio
import random


async def long(x):
     duration = random.randint(2, 5)
     await asyncio.sleep(duration)
     print(f"long is done {x} slept for {duration} seconds")


loop = asyncio.get_event_loop()

task1 = loop.create_task(long(1))
task2 = loop.create_task(long(2))
task3 = loop.create_task(long(3))

print('after asyncio.run')

loop.run_until_complete(asyncio.gather(task1, task2, task3))

So here run_until_complete will wait untill all three tasks complete  or 
cancel for some reasons (then exception happens. btw we don't handle it 
in example).


In your example you use 'asyncio.run'. Internally it does things like to 
this

def run(ft)
     loop = asyncio.get_event_loop()
     task = loop.create_task(ft())
     loop.run_until_complete(asyncio.gather(task))

Of course real asyncio.run is much more complicated but model is same, i 
think.

It creates loop and future and 'run_until_complete' for future/task in 
the loop.

That's why you never get 'print' before asyncio.run finishes.


You also can split run_until_complete and gather.

s = asyncio.gather(task1, task2, task3)
print('after asyncio.run')
loop.run_until_complete(s)

Then 'gather' starts schedule all tasks before 'print' executes. For 
example, if you have some network IO operations instead this 'print' ... 
maybe few of your async tasks are done during this time.


>
>> 30.03.2022 19:10, Larry Martell пишет:
>>> import asyncio
>>> import time
>>>
>>> async def long():
>>>       for i in range(100):
>>>          time.sleep(10)
>>>
>>> asyncio.run(long())
>>> print('after asyncio.run')
>> --
>> https://mail.python.org/mailman/listinfo/python-list


More information about the Python-list mailing list