Reputation: 993
I cannot figure out this behavior. I'm using Python 3.6.4.
In here, I have 4 async functions. In test1
, I call asyncio.ensure_future(TestAsync())
and turn the coroutine into a task. On the others (test2
, test3
, test4
) I didn't call ensure_future()
, I just created the coroutine and passed them into a list and then passed the list into the event loop.
I left test1
out of the list I passed into the event loop. So I was under the impression test1 would not run, but in my output, it clearly did. Can someone explain to me why test1
still outputed when it wasn't inside the event loop?
import asyncio
import random
async def TestAsync():
print("Test 1 started")
wait = random.randint(1, 10)
await asyncio.sleep(wait)
print("Test 1 done awaited: " + str(wait))
async def TestAsync2():
print("Test 2 started")
wait = random.randint(1, 10)
await asyncio.sleep(wait)
print("Test 2 done awaited: " + str(wait))
async def TestAsync3():
print("Test 3 started")
wait = random.randint(1, 10)
await asyncio.sleep(wait)
print("Test 3 done awaited: " + str(wait))
async def TestAsync4():
print("Test 4 started")
wait = random.randint(1, 10)
await asyncio.sleep(wait)
print("Test 4 done awaited: " + str(wait))
test1 = asyncio.ensure_future(TestAsync())
test2 = TestAsync2()
test3 = TestAsync3()
test4 = TestAsync4()
tasklist = [test2, test3, test4]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasklist))
Output
Test 1 started. <--This is the one wasn't in the event loop
Test 3 started
Test 4 started
Test 2 started
Test 4 done awaited: 1
Test 3 done awaited: 2
Test 1 done awaited: 7
Test 2 done awaited: 10
Upvotes: 2
Views: 1666
Reputation: 155046
As others pointed out, ensure_future
will add the task to the default event loop, to run at the next opportunity. ensure_future
converts an arbitrary awaitable into an asyncio.Future
, which in case of a coroutine object is accomplished by wrapping it into a Task
(a subclass of Future
) with a call to create_task
. The task will run the next time the event loop spins regardless of whether the code that calls ensure_future
stores a reference to the returned future.
The other important thing to notice is that run_until_complete(x)
means "submit x
to the event loop and run the loop until x
completes", it does nothing to prevent tasks added before the call to run_until_complete
from running.
In Python 3.7, there is a new function asyncio.run
that creates a new event loop and submits the given coroutine to it. Replacing asyncio.run_until_complete
with asyncio.run
would produce the behavior you expect.
Upvotes: 3
Reputation: 114330
From the docs:
Calling a coroutine does not start its code running – the coroutine object returned by the call doesn’t do anything until you schedule its execution. There are two basic ways to start it running: call
await coroutine
oryield from coroutine
from another coroutine (assuming the other coroutine is already running!), or schedule its execution using theensure_future()
function or theAbstractEventLoop.create_task()
method.
As @dirn's answer implies, ensure_future
by itself does not start the task unless there is an event loop already running. However, the subsequent loop kickoff with run_until_complete
does start the task.
Upvotes: 0
Reputation: 20739
From the docs for ensure_future
:
Schedule the execution of a coroutine object
When you call ensure_future
, the coroutine is schedule and will be eligible to run whenever the event loop is running.
Upvotes: 0