I have an async function e.g. a. It is used as b = await a()
How can I record the time it takes for this a() to finish its execution?
You can use time.monotonic().
For example:
import asyncio
import time
async def a():
await asyncio.sleep(3)
return 1
async def main():
start_time = time.monotonic()
b = await a()
print('time: ', time.monotonic() - start_time)
asyncio.run(main())
Prints:
time: 3.002365263993852
Related
I have the following function to call s(c) every 24 hours.
def schedule_next_sync():
t = datetime.datetime.now()
t = t.replace(hour=0) + datetime.timedelta(hours=24)
def wrapper():
s(c)
schedule_next_sync()
tornado.ioloop.IOLoop.current().add_timeout(datetime.datetime.timestamp(t), wrapper)
However, s() will be changed to an async function.
async def s(c):
How to update schedule_next_sync for async function? Should run s() synchronously? Or change schedule_next_sync() to an async function?
Once s is async, you could use asyncio.sleep() instead of the lower-level add_timeout():
async def schedule_next_sync():
async def call_forever():
while True:
await asyncio.sleep(1)
await s(c)
tornado.ioloop.IOLoop.current().create_task(call_forever())
If you really want to do it with timeouts, something like this should work:
def schedule_next_sync():
t = datetime.datetime.now() + datetime.timedelta(seconds=1)
def wrapper():
loop = asyncio.get_running_loop()
task = loop.create_task(s(c))
task.add_done_callback(lambda _: schedule_next_sync())
loop = tornado.ioloop.IOLoop.current()
loop.add_timeout(datetime.datetime.timestamp(t), wrapper)
I get this error:
D:\pythonstuff\demo.py:28: DeprecationWarning: The explicit passing of coroutine objects to asyncio.wait() is deprecated since Python 3.8, and scheduled for removal in Python 3.11.
await asyncio.wait([
Waited 1 second!
Waited 5 second!
Time passed: 0hour:0min:5sec
Process finished with exit code 0
When I run the code:
import asyncio
import time
class class1():
async def function_inside_class(self):
await asyncio.sleep(1)
print("Waited 1 second!")
async def function_inside_class2(self):
await asyncio.sleep(5)
print("Waited 5 second!")
def tic():
global _start_time
_start_time = time.time()
def tac():
t_sec = round(time.time() - _start_time)
(t_min, t_sec) = divmod(t_sec,60)
(t_hour,t_min) = divmod(t_min,60)
print('Time passed: {}hour:{}min:{}sec'.format(t_hour,t_min,t_sec))
object = class1()
async def main():
tic()
await asyncio.wait([
object.function_inside_class(),
object.function_inside_class2()
])
tac()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
Are there any good alternatives to asyncio.wait? I don't want a warning in console every time I launch my application.
Edit: I don't want to just hide the error, that's bad practice, and I'm looking for other ways to do the same or a similar thing, not another async library to restore the old functionality.
You can just call it this way as it recommends in the docs here
Example from the docs:
async def foo():
return 42
task = asyncio.create_task(foo())
done, pending = await asyncio.wait({task})
So your code would become:
await asyncio.wait([
asyncio.create_task(object.function_inside_class()),
asyncio.create_task(object.function_inside_class2())
])
In the following code, are get_all_details_1 and get_all_details_2 behave the same?
async def get_details(category, limit):
async with limit:
# ...
limit = asyncio.Semaphore(4)
a_list = .... # a big list
async def get_all_details_1():
b_list = await asyncio.gather(*[get_details(x, limit) for x in a_list])
# ....
async def get_all_details_2():
b_list = [await get_details(x, limit) for x in a_list]
# ....
Absolutely not! Example:
import asyncio
import time
async def slowtask():
await asyncio.sleep(1)
async def gather():
await asyncio.gather(*[slowtask() for _ in range(10)])
async def listcomp():
[await slowtask() for _ in range(10)]
start = time.time()
asyncio.run(gather())
print("gather", time.time() - start)
start = time.time()
asyncio.run(listcomp())
print("listcomp", time.time() - start)
gives us:
gather 1.0030405521392822
listcomp 10.015443325042725
asyncio.gather properly allows multiple async tasks to run asynchronously while the list comprehension awaits one after the other, leading to effectively serial code.
I have a function that I want to call every minute for instance and my code look like this:
async def fun1():
print('fun1')
await asyncio.sleep(30)
async def fun2():
print('fun2')
await asyncio.sleep(10)
async def fun3():
print('fun3')
async def main():
global loop
loop.create_task(fun1())
loop.create_task(fun2())
while True:
await fun3()
await asyncio.sleep(1)
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
but it does not print anything. I would like my function to be called every 10 seconds for instance. It looks like fun2 is waiting for fun1 to finish instead of triggering every 30 seconds and 10 seconds respectively...
Any idea why pelase?
Currently, fun1 and fun2 will only print once each since neither contain a loop. Add a loop to make them each print every 10/30 seconds.
import asyncio
async def fun1():
while True:
print('fun1')
await asyncio.sleep(30)
async def fun2():
while True:
print('fun2')
await asyncio.sleep(10)
async def fun3():
print('fun3')
async def main():
global loop
loop.create_task(fun1())
loop.create_task(fun2())
while True:
await fun3()
await asyncio.sleep(1)
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
I was wondering how I could use asyncio to handle tasks similar to what nodeJS does. I want to run tasks at the same time without opening threads.
Example:
import asyncio
#asyncio.coroutine
def my_coroutine(task_name, seconds_to_sleep=3):
print('{0} sleeping for: {1} seconds'.format(task_name, seconds_to_sleep))
yield from asyncio.sleep(seconds_to_sleep)
print('{0} is finished'.format(task_name))
loop = asyncio.get_event_loop()
tasks = [
my_coroutine('task1', 4),
my_coroutine('task2', 3),
my_coroutine('task3', 2)]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
will output:
task1 sleeping for: 4 seconds
task2 sleeping for: 3 seconds
task3 sleeping for: 2 seconds
task3 is finished
task2 is finished
task1 is finished
but when I try to do it with a different task it won't work like that.
import asyncio
import timeit
#asyncio.coroutine
def my_coroutine(task_name):
print('order placed for ' + task_name)
print(timeit.timeit('1 + 3 ', number=50000000))
print(task_name + ' done')
loop = asyncio.get_event_loop()
tasks = [
my_coroutine('task1'),
my_coroutine('task2'),
my_coroutine('task3')]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
outputs
order placed for task2
0.6677237730912453
task2 done
order placed for task1
0.6627442526498016
task1 done
order placed for task3
0.665618849882418
task3 done
asyncio doesn't run things in parallel. It runs one task until it awaits, then moves on to the next. The sleeps in your first example are what make the tasks yield control to each other. Your second example doesn't await anything, so each task runs until completion before the event loop can give control to another task.
If you add something awaitable (e.g., asyncio.sleep) into your coroutine, each one will yield control and give the others a chance to run.
#asyncio.coroutine
def my_coroutine(task_name):
print('order placed for ' + task_name)
yield from asyncio.sleep(0) # Another coroutine will resume here.
print(timeit.timeit('1 + 3 ', number=50000000))
yield from asyncio.sleep(0) # Another coroutine will resume here.
print(task_name + ' done')
The asyncio documentation says below so asyncio tasks run concurrently but not parallelly.
asyncio is a library to write concurrent code using the async/await
syntax.
And, #asyncio.coroutine is deprecated since Python 3.7.14 and removed since Python 3.11.0 so instead, you should use async as shown below:
# #asyncio.coroutine
async def test():
print("Test")
And for example, with this code below:
import asyncio
async def test1():
for _ in range(0, 3):
print("Test1")
async def test2():
for _ in range(0, 3):
print("Test2")
async def test3():
for _ in range(0, 3):
print("Test3")
async def call_tests():
await asyncio.gather(test1(), test2(), test3())
asyncio.run(call_tests())
test1(), test2() and test3() are run serially as shown below:
Test1 # 0 second
Test1 # 0 second
Test1 # 0 second
Test2 # 0 second
Test2 # 0 second
Test2 # 0 second
Test3 # 0 second
Test3 # 0 second
Test3 # 0 second
And, if using await asyncio.sleep(1) in them as shown below:
import asyncio
async def test1():
for _ in range(0, 3):
print("Test1")
await asyncio.sleep(1) # Here
async def test2():
for _ in range(0, 3):
print("Test2")
await asyncio.sleep(1) # Here
async def test3():
for _ in range(0, 3):
print("Test3")
await asyncio.sleep(1) # Here
async def call_tests():
await asyncio.gather(test1(), test2(), test3())
asyncio.run(call_tests())
They are run alternately sleeping 1 second each time as shown below:
Test1 # 1 second
Test2 # 1 second
Test3 # 1 second
Test1 # 2 seconds
Test2 # 2 seconds
Test3 # 2 seconds
Test1 # 3 seconds
Test2 # 3 seconds
Test3 # 3 seconds
And, if using await asyncio.sleep(0) in them as shown below:
import asyncio
async def test1():
for _ in range(0, 3):
print("Test1")
await asyncio.sleep(0) # Here
async def test2():
for _ in range(0, 3):
print("Test2")
await asyncio.sleep(0) # Here
async def test3():
for _ in range(0, 3):
print("Test3")
await asyncio.sleep(0) # Here
async def call_tests():
await asyncio.gather(test1(), test2(), test3())
asyncio.run(call_tests())
They are run alternately without sleeping as shown below:
Test1 # 0 second
Test2 # 0 second
Test3 # 0 second
Test1 # 0 second
Test2 # 0 second
Test3 # 0 second
Test1 # 0 second
Test2 # 0 second
Test3 # 0 second