Home > database >  How to run multiple async functions with "loop.run_until_complete()" alternately in Python
How to run multiple async functions with "loop.run_until_complete()" alternately in Python

Time:11-08

I'm trying to run 2 async functions test1() and test2() with loop.run_until_complete() alternately in Python as shown below:

import asyncio

async def test1():
    for _ in range(3):
        print("Test1")
        await asyncio.sleep(1)
        
async def test2():
    for _ in range(3):
        print("Test2")
        await asyncio.sleep(1)

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

loop.run_until_complete(test1()) # Here
loop.run_until_complete(test2()) # Here

But as shown below, they don't run with loop.run_until_complete() alternately:

Test1
Test1
Test1
Test2
Test2
Test2

I know that if I use loop.run_forever() with loop.create_task() as shown below:

import asyncio

async def test1(loop):
    for _ in range(3):
        print("Test1")
        await asyncio.sleep(1)
        loop.stop() # Extra code to stop "loop.run_forever()"
        
async def test2(loop):
    for _ in range(3):
        print("Test2")
        await asyncio.sleep(1)
        loop.stop() # Extra code to stop "loop.run_forever()"

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

loop.create_task(test1(loop)) # Here
loop.create_task(test2(loop)) # Here
loop.run_forever() # Here

I can run them alternately as shown below but loop.run_forever() runs forever so to stop loop.run_forever(), the extra code loop.stop() is needed which is troublesome. In addition, I know that asyncio.gather() can also run them alternately but it needs await which I don't want:

Test1
Test2
Test1
Test2
Test1
Test2

So, how can I run them with loop.run_until_complete() alternately?

CodePudding user response:

If you wouldn't insist on the loop.run_until_complete you can achieve what you want to get by using the asyncio.gather functionality, just like so:

import asyncio

async def test1():
    for _ in range(3):
        print("Test1")
        await asyncio.sleep(1)

async def test2():
    for _ in range(3):
        print("Test2")
        await asyncio.sleep(1)

async def main():
    tasks = [test1(), test2()]
    new_items = await asyncio.gather(*tasks)
    return new_items


if __name__ == '__main__':
    results = asyncio.run(main())

and the results would be as you'd expect-

==> python3 stack_overflow.py 
Test1
Test2
Test1
Test2
Test1
Test2

CodePudding user response:

You can run 2 async functions test1() and test2() alternately by calling the intermediate async function call_tests() with loop.run_until_complete() as shown below. You also need to use asyncio.get_running_loop() and loop.create_task() in call_tests() and await is needed for the last loop.create_task() to run them alternately as shown below:

import asyncio

async def test1():
    for _ in range(3):
        print("Test1")
        await asyncio.sleep(1)
        
async def test2():
    for _ in range(3):
        print("Test2")
        await asyncio.sleep(1)

async def call_tests(): # Here
    loop = asyncio.get_running_loop() # Here
    loop.create_task(test1()) # Here
    await loop.create_task(test2()) # "await" is needed

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

loop.run_until_complete(call_tests()) # Call "call_tests()"

Finally, you can run them alternately as shown below:

Test1
Test2
Test1
Test2
Test1
Test2

Be careful, if using await for the first and last loop.create_task() both as shown below:

# ...

async def call_tests():
    loop = asyncio.get_running_loop()
    await loop.create_task(test1()) # Here
    await loop.create_task(test2()) # Here

# ...

You cannot run them alternately as shown below:

Test1
Test1
Test1
Test2
Test2
Test2

And, if using await for the first loop.create_task() as shown below:

# ...

async def call_tests():
    loop = asyncio.get_running_loop()
    await loop.create_task(test1()) # Here
    loop.create_task(test2())

# ...

You cannot run them alternately and the last loop.create_task() is exited without completed as shown below:

Test1
Test1
Test1
Test2

And, if not using await for the first and last loop.create_task() both as shown below:

# ...

async def call_tests():
    loop = asyncio.get_running_loop()
    loop.create_task(test1()) # No "await"
    loop.create_task(test2()) # No "await"

# ...

You can run them alternately but the first and last loop.create_task() both are exited without completed as shown below:

Test1
Test2

In addition, if using asyncio.gather(), call_tests() is not needed as shown below:

import asyncio

async def test1():
    for _ in range(3):
        print("Test1")
        await asyncio.sleep(1)
        
async def test2():
    for _ in range(3):
        print("Test2")
        await asyncio.sleep(1)

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

loop.run_until_complete(asyncio.gather(test1(), test2()))

Then, you can run them alternately as shown below:

Test1
Test2
Test1
Test2
Test1
Test2
  • Related