human
Solve the following python programming problem given the following code:
# %load asyncio_generator.py
import asyncio

@asyncio.coroutine
def outer():
    print('in outer')
    print('waiting for result1')
    result1 = yield from phase1()
    print('waiting for result2')
    result2 = yield from phase2(result1)
    return (result1, result2)

@asyncio.coroutine
def phase1():
    print('in phase1')
    return 'result1'

@asyncio.coroutine
def phase2(arg):
    print('in phase2')
    return 'result2 derived from {}'.format(arg)

event_loop = asyncio.get_event_loop()
try:
    return_value = event_loop.run_until_complete(outer())
    print('return value: {!r}'.format(return_value))
finally:
    event_loop.close()

!python asyncio_generator.py

Given the above code, generate some markdown instructions for it.
assistant
#Here is the Python program you requested.

The await keyword is used instead of adding the new coroutines to the loop, because control flow is already inside of a coroutine being managed by the loop so it isn’t necessary to tell the loop to manage the new coroutines.
Generators Instead of Coroutines
Coroutine functions are a key component of the design of asyncio. They provide a language construct for stopping the execution of part of a program, preserving the state of that call, and re-entering the state at a later time, which are all important capabilities for a concurrency framework.
Python 3.5 introduced new language features to define such coroutines natively using async def and to yield control using await, and the examples for asyncio take advantage of the new feature. Earlier versions of Python 3 can use generator functions wrapped with the asyncio.coroutine() decorator and yield from to achieve the same effect.