I was testing Python3.7 async/await functions and written simple code like this:
import asyncio
import time
async def first():
print('FIRST')
async def second():
time.sleep(2)
print('SECOND')
async def main():
asyncio.create_task(second())
asyncio.create_task(second())
asyncio.create_task(second())
asyncio.create_task(first())
asyncio.create_task(first())
asyncio.create_task(first())
asyncio.run(main())
When I run this code, it prints this:
SECOND
SECOND
SECOND
FIRST
FIRST
FIRST
Which is not what I expected. I thought FIRSTs should be printed first because they don't have delay. However, create_task waits until second() is finished before proceeding to text async task.
Is it possible to execute async tasks without making them to wait each other?
You have to use the gather() function and modify a bit your code. You also have to use the await keyword.
import asyncio
import time
async def first():
print('FIRST')
await asyncio.sleep(1)
print('SECOND')
async def main():
await asyncio.gather(first(), first(), first())
asyncio.run(main())
You can't use the time.sleep() function since it is not asynchronous, resulting in an actual sleep for the entire program. use the asyncio.sleep() function and await it so it runs asynchronously:
import asyncio
async def first():
print('FIRST')
async def second():
# The Fix:
await asyncio.sleep(2)
print('SECOND')
async def main():
await asyncio.gather(
second(),
second(),
second(),
first(),
first(),
first()
)
if __name__ == '__main__':
asyncio.run(main())
The result should be:
FIRST
FIRST
FIRST
SECOND
SECOND
SECOND
Related
I have a function in python that should kick off an asynchronous 'slow' function, and return before that 'slow' function completes.
async def doSomethingSlowButReturnQuickly():
asyncio.create_task(_mySlowFunction())
return "Returning early! Slow function is still running."
async def _mySlowFunction():
// Do some really slow stuff that isn't blocking for our other function.
Running this still seems to cause the 'return' to not happen until AFTER my async task has completed.
How do I correct this?
(And apologies, this is day #2 writing Python)
This isn't exactly what you asked, but you can run code in an async executor and continue while it is still running:
from concurrent.futures import ThreadPoolExecutor
from time import sleep
def blocking_code():
sleep(2)
print('inner')
return 'result'
def main():
with ThreadPoolExecutor(max_workers=1) as executor:
future = executor.submit(blocking_code)
print('after')
print(future.result())
return
if __name__ == '__main__':
main()
Output:
after
inner
result
Maybe it's a dull answer, but if you want to stick with asyncio, I would advise to be sure using await in everyasync def functions.
async def doSomethingSlowButReturnQuickly():
task = asyncio.create_task(_mySlowFunction())
# must await something here!
return "Returning early! Slow function is still running."
async def _mySlowFunction():
# must await something here
If you don't need to get the slow function return value, why don't you directly call the two coroutines from a main function?
import asyncio
async def doSomethingSlowButReturnQuickly():
await asyncio.sleep(0.1) # must await something here!
return "Returning early! Slow function is still running."
async def _mySlowFunction():
await asyncio.sleep(10) # must await something here
async def main():
tasks = [_mySlowFunction(), doSomethingSlowButReturnQuickly()]
asyncio.gather(*tasks)
asyncio.run(main())
I'm trying to run some code asynchronously. My expectation is that the test coroutine should not block the print(running first) statement. This is because I've dispatched it to the event loop, and should be seeing the output of this command logged first.
import asyncio
async def test():
await asyncio.sleep(5)
print("I should run second")
asyncio.run(test())
print('running first')
Does anyone have any tips on how to how this code run so that print('running first') is ran before print("I should run second")? I believe this code should be non-blocking, so I'm confused as to why the order of print messages isn't matching my expectation.
I believe this is what you want:
import asyncio
async def test():
await asyncio.sleep(5)
print("I should run second")
async def main():
task1 = asyncio.create_task(test())
print('running first')
await task1
asyncio.run(main())
A more detail explaination:
asyncio.run() will try to wait all of the task inside it to finish before it continues.
In your code, you are running asyncio.run(test()) first and it will continue ONLY IF test() IS ENDED and you awaited the sleep. so test() will end after the sleep and run the print then the main print.
This is why your code delay so long before running. The solution to it is simple. Fire the task without waiting, which is what asyncio.create_task() is doing, I created a task, fire it but wait it at the end.
btw normally when you are using async, you will have a ton of task in a list. If you want to wait it as a list you should use gather():
import asyncio
async def test():
await asyncio.sleep(5)
print("I should run second")
async def main():
task_list = []
for _ in range(100):
task_list.append(asyncio.create_task(test()))
print('running first')
await asyncio.gather(*task_list)
asyncio.run(main())
I want to make a timer which is started in a normal function, but in the timer function, it should be able to call an async function
I want to do something like this:
startTimer()
while True:
print("e")
def startTimer(waitForSeconds: int):
# Wait for `waitForSeconds`
await myAsyncFunc()
async def myAsyncFunc():
print("in my async func")
Where the while True loop should do its stuff and after waitForSeconds the timer the async function should execute an other async function, but waiting shouldn't block any other actions and doesn't need to be awaited
If something isn't understandable, I'm sorry, I'll try to explain it then
Thanks
If you want to run your synchronous and asynchronous code in parallel, you will need to run one of them in a separate thread. For example:
def sync_code():
while True:
print("e")
async def start_timer(secs):
await asyncio.sleep(secs)
await async_func()
async def main():
asyncio.create_task(start_timer(1))
loop = asyncio.get_event_loop()
# use run_in_executor to run sync code in a separate thread
# while this thread runs the event loop
await loop.run_in_executor(None, sync_code)
asyncio.run(main())
If the above is not acceptable for you (e.g. because it turns the whole program into an asyncio program), you can also run the event loop in a background thread, and submit tasks to it using asyncio.run_coroutine_threadsafe. That approach would allow startTimer to have the signature (and interface) like you wanted it:
def startTimer(waitForSeconds):
loop = asyncio.new_event_loop()
threading.Thread(daemon=True, target=loop.run_forever).start()
async def sleep_and_run():
await asyncio.sleep(waitForSeconds)
await myAsyncFunc()
asyncio.run_coroutine_threadsafe(sleep_and_run(), loop)
async def myAsyncFunc():
print("in my async func")
startTimer(1)
while True:
print("e")
I'm pretty sure that you are familiar with concurent processing, but you didn't show exactly what you want. So if I understand you correctly you want to have 2 processes. First is doing only while True, and the second process is the timer(waits e.g. 5s) and it will call async task. I assume that you are using asyncio according to tags:
import asyncio
async def myAsyncFunc():
print("in my async func")
async def call_after(delay):
await asyncio.sleep(delay)
await myAsyncFunc()
async def while_true():
while True:
await asyncio.sleep(1) # sleep here to avoid to large output
print("e")
async def main():
task1 = asyncio.create_task(
while_true())
task2 = asyncio.create_task(
call_after(5))
# Wait until both tasks are completed (should take
# around 2 seconds.)
await task1
await task2
asyncio.run(main())
I wrote the following code:
import asyncio
async def write_after(pause,text):
print('begin')
await asyncio.sleep(pause)
print(text)
async def main():
await write_after(1,'Hello...')
await write_after(2,'...world')
asyncio.run(main())
As result I got:
begin
Hello...
begin
...world
with pauses right after begins. I was wondering why result isn't:
begin
begin
Hello...
...world
like executing program that uses tasks.
Basically what's happening is you're waiting for the first result to finish, then starting the second function call and waiting for that result. I think what you're expecting is something like this:
import asyncio
async def write_after(pause,text):
print('begin')
await asyncio.sleep(pause)
print(text)
async def main():
await asyncio.gather(
write_after(1,'Hello...'),
write_after(2,'...world')
)
asyncio.run(main())
This will launch both coroutines concurrently and wait for the results of each. The result will be:
begin
begin
Hello...
...world
#kingkupps analysis is correct. Unfortuantely, myy asyncio module has no run method (Python 3.7), so as an alternative:
import asyncio
async def write_after(pause,text):
print('begin')
await asyncio.sleep(pause)
print(text)
def main():
loop = asyncio.get_event_loop()
loop.run_until_complete(
asyncio.gather(
write_after(1, 'Hello'),
write_after(2, '...world')
)
)
main()
Prints:
begin
begin
Hello
...world
Assume the following code:
import asyncio
import time
async def say_after():
for i in range(10):
await asyncio.sleep(1)
print("first function")
async def say_after2():
for i in range(10):
await asyncio.sleep(1)
print("second function")
async def main():
await say_after()
await say_after2()
asyncio.run(main())
It would first print "first function" 10 times and after function say_after is finished then it would print "second function" 10 times. now i want to run both functions at the same time like 2 threads (like parallel) with asycio not sequential executing. how do i do it?