asyncio solves this by creating set of coroutines that use the yield from syntax to return control to the event loop immediately the event loop takes care of checking whether the blocking call has completed and performing any subsequent tasksjust like we did manually in the previous section asyncio in action canonical example of blocking function is the time sleep call let' use the asynchronous version of this call to illustrate the basics of an asyncio event loopimport asyncio import random @asyncio coroutine def random_sleep(counter)delay random random( print("{sleeps for { fsecondsformat(counterdelay)yield from asyncio sleep(delayprint("{awakensformat(counter)@asyncio coroutine def five_sleepers()print("creating five tasks"tasks asyncio async(random_sleep( )for in range( )print("sleeping after starting five tasks"yield from asyncio sleep( print("waking and waiting for five tasks"yield from asyncio wait(tasksasyncio get_event_loop(run_until_complete(five_sleepers()print("done five tasks"this is fairly basic examplebut it covers several features of asyncio programming it is easiest to understand in the order that it executeswhich is more or less bottom to top the second last line gets the event loop and instructs it to run future until it is finished the future in question is named five_sleepers once that future has done its workthe loop will exit and our code will terminate as asynchronous programmerswe don' need to know too much about what happens inside that run_ until_complete callbut be aware that lot is going on it' souped up coroutine version of the futures loop we wrote in the previous that knows how to deal with iterationexceptionsfunction returnsparallel callsand more