now look little more closely at that five_sleepers future ignore the decorator for few paragraphswe'll get back to it the coroutine first constructs five instances of the random_sleep future the resulting futures are wrapped in an asyncio async taskwhich adds them to the loop' task queue so they can execute concurrently when control is returned to the event loop that control is returned whenever we call yield from in this casewe call yield from asyncio sleep to pause execution of this coroutine for two seconds during this breakthe event loop executes the tasks that it has queued upnamely the five random_sleep futures these coroutines each print starting messagethen send control back to the event loop for specific amount of time if any of the sleep calls inside random_sleep are shorter than two secondsthe event loop passes control back into the relevant futurewhich prints its awakening message before returning when the sleep call inside five_sleepers wakes upit executes up to the next yield from callwhich waits for the remaining random_sleep tasks to complete when all the sleep calls have finished executingthe random_sleep tasks returnwhich removes them from the event queue once all five of those are completedthe asyncio wait call and then the five_sleepers method also return finallysince the event queue is now emptythe run_until_complete call is able to terminate and the program ends the asyncio coroutine decorator mostly just documents that this coroutine is meant to be used as future in an event loop in this casethe program would run just fine without the decorator howeverthe asyncio coroutine decorator can also be used to wrap normal function (one that doesn' yieldso that it can be treated as future in this casethe entire function executes before returning control to the event loopthe decorator just forces the function to fulfill the coroutine api so the event loop knows how to handle it reading an asyncio future an asyncio coroutine executes each line in order until it encounters yield from statementat which point it returns control to the event loop the event loop then executes any other tasks that are ready to runincluding the one that the original coroutine was waiting on whenever that child task completesthe event loop sends the result back into the coroutine so that it can pick up executing until it encounters another yield from statement or returns this allows us to write code that executes synchronously until we explicitly need to wait for something this removes the nondeterministic behavior of threadsso we don' need to worry nearly so much about shared state