Alright so this is really cool, let me show you something:
loop = asyncio.get_event_loop()
api = MyAPIToSomeCoolChatProgram()
def my_done_callback(fut):
exc = fut.exception()
if exc:
print(fut.my_custom_attribute, 'raised an exception!')
import traceback
traceback.print_exc(exc) # dumps a "Traceback (most recent call last):" message to stderr
print(fut.my_custom_attribute, 'completed, returned', repr(fut.result()))
fut1 = asyncio.ensure_future(api.send_friend_request(my_best_friend))
fut1.my_custom_attribute = 'fut1 (add a friend)'
fut1.add_done_callback(my_done_callback)
fut2 = asyncio.ensure_future(api.post_text_message('Hello everybody!'))
fut2.my_custom_attribute = 'fut2 (send a message)'
fut2.add_done_callback(my_done_callback)
print('Done creating the futures')
loop.run_forever()
Output:
Done creating the futures
fut1 (add a friend request) completed, returned '200 OK'
fut2 (send a message) completed, returned '200 OK'
Note that they may appear in any order. You can call coroutines from non asynchronous code by wrapping the coroutine (the return value from the coroutine function) in a future (or more accurately a Task
which is a subclass of Future
). This coroutine will now run in the background. You can add a callback to the future which will be called when it finishes, passed one argument: the future object itself. Look up futures in the asyncio documentation if you'd like to learn more about them (also check out Coroutines and Tasks).
Anyway, those callbacks can do anything you want, including starting other tasks.
def when_done_logging_in(self, fut):
self.login_info = fut.result() # note: calling fut.result() if the login coroutine raised an exception will reraise the exception here.
next_fut = asyncio.ensure_future(self.send_friend_request(fut.friend_request_to_send))
# do something with next_fut here (or don't if you don't care about the result)
def login_and_send_friend_request(self, email, friend):
fut = asyncio.ensure_future(self.query_login(email))
fut.friend_request_to_send = friend
fut.add_done_callback(self.when_done_logging_in)
Of course you could also do that with:
async def login_and_send_friend_request(self, email, friend):
self.login_info = await self.query_login(email)
await self.send_friend_request(friend)
which would be better because any exceptions are actually handled properly instead of simply being ignored. You could also do this, if you know the email in advance (which you may not):
def __init__(self, whatever_args_you_might_have_here, email):
...
self.login_info = None
self.email = email
async def send_friend_request(self, uid):
if self.login_info is None:
await self.query_login(self.email) # if you end up doing this you should probably make this not take a parameter and just use self.email instead
do_send_friend_request_stuff()
Of course you might not know the email until after the object is created, in which case you could either initialize it to None until some login function is called, or use one of the first two ways.
If you wanted to execute a list of functions in sequence, you could do the following:
def execute_coros_in_sequence(list_of_coros):
fut=asyncio.ensure_future(list_of_coros[0])
if len(list_of_coros) > 1:
# there is probably a better way to do this
fut.remaining_coros=list_of_coros[1:]
fut.add_done_callback(lambda fut: execute_coros_in_sequence(fut.remaining_coros))
but probably a better way to do it would be just to make an async def function call them all, because that way you get exception handling etc. without a lot of overcomplication. A better way to do it, if you wanted it as a future (which you can also store as an attribute of an object and query to see if it's done yet), would be this:
class API:
async def login(self):
pass
async def logout(self):
pass
async def do_fun_stuff(self):
pass
async def test_api(api):
api.login()
api.do_fun_stuff()
api.logout()
fut=asyncio.create_task(test_part_of_api(API()))
(By the way, asyncio.ensure_future()
first checks to see if its argument is already a future, and if not, calls asyncio.create_task()
.)
but the future api is really cool and I wanted to show it to you. There are uses for it, and I can pretty much guarantee you're going to need both of these approaches in order to do anything complex.
Sorry for the wall of text disorganized answer. I'm a bit new here. I just think asyncio is really cool.