import asyncio
import functools


def callback(arg, *, kwarg='default'):
    print('callback invoked with {} and {}'.format(arg, kwarg))


async def main(loop):
    print('registering callbacks')
    loop.call_soon(callback, 1)
    wrapped = functools.partial(callback, kwarg='not default')
    loop.call_soon(wrapped, 2)

    await asyncio.sleep(0.1)


event_loop = asyncio.get_event_loop()
try:
    print('entering event loop')
    event_loop.run_until_complete(main(event_loop))
finally:
    print('closing event loop')
    event_loop.close()

"""
Explanation: In addition to managing coroutines and I/O callbacks, the asyncio event loop can schedule calls to regular functions based on the timer value kept in the loop.
Scheduling a Callback “Soon”
If the timing of the callback does not matter, call_soon() can be used to schedule the call for the next iteration of the loop. Any extra positional arguments after the function are passed to the callback when it is invoked. To pass keyword arguments to the callback, use partial() from the functools module
End of explanation
"""


# %load asyncio_call_later.py
import asyncio


def callback(n):
    print('callback {} invoked'.format(n))


async def main(loop):
    print('registering callbacks')
    loop.call_later(0.2, callback, 1)
    loop.call_later(0.1, callback, 2)
    loop.call_soon(callback, 3)

    await asyncio.sleep(0.4)


event_loop = asyncio.get_event_loop()
try:
    print('entering event loop')
    event_loop.run_until_complete(main(event_loop))
finally:
    print('closing event loop')
    event_loop.close()

!python asyncio_call_later.py

"""
Explanation: The callbacks are invoked in the order they are scheduled.
Scheduling a Callback with a Delay
To postpone a callback until some time in the future, use call_later(). The first argument is the delay in seconds and the second argument is the callback.
End of explanation
"""


# %load asyncio_call_at.py
import asyncio
import time


def callback(n, loop):
    print('callback {} invoked at {}'.format(n, loop.time()))


async def main(loop):
    now = loop.time()
    print('clock time: {}'.format(time.time()))
    print('loop  time: {}'.format(now))

    print('registering callbacks')
    loop.call_at(now + 0.2, callback, 1, loop)
    loop.call_at(now + 0.1, callback, 2, loop)
    loop.call_soon(callback, 3, loop)

    await asyncio.sleep(1)


event_loop = asyncio.get_event_loop()
try:
    print('entering event loop')
    event_loop.run_until_complete(main(event_loop))
finally:
    print('closing event loop')
    event_loop.close()

!python asyncio_call_at.py

"""
Explanation: In this example, the same callback function is scheduled for several different times with different arguments. The final instance, using call_soon(), results in the callback being invoked with the argument 3 before any of the time-scheduled instances, showing that “soon” usually implies a minimal delay.
Scheduling a Callback for a Specific Time
It is also possible to schedule a call to occur at a specific time. The loop uses a monotonic clock, rather than a wall-clock time, to ensure that the value of “now” never regresses. To choose a time for a scheduled callback it is necessary to start from the internal state of that clock using the loop’s time() method.
End of explanation
"""


