Следует различать вызов асинхронной функции и её выполнение.
В твоём случае, вызов work(p) завершится немедленно (без входа в тело функции), и вернёт объект Future.
Этот объект описывает выполняемую асинхронную операцию (ввод-вывод, выполнение функции и т.п.).
Затем следует запланировать выполнение этого объекта в рамках цикла реактора (loop в т ерминах asyncio).
Для этого можно использовать два способа. Если ты находишься в синхронном коде, ты должен использовать loop.create_task() (или более старую функцию, loop.ensure_future()).
Если ты находишься в асинхронном коде, то твоя текущая функция уже завёрнута в свой собственный Future, и уже выполняется в рамках цикла реактора. Тогда ты можешь использовать await для того, чтобы "уступить место" вызываемой функции - запланировать её выполнение в рамках того же цикла, что и вызывающая функция, а вызывающую функцию приостановить до завершения выполнения вызываемой. Либо, если тебе не требуется дожидаться результата выполнения вызываемой функции, можешь также использовать первый способ.
Таким образом, когда ты "вызываешь асинхронную функцию через await", ты на самом деле получаешь future-объект и тут же планируешь его выполнение.
Т.е.
X = await foo()
будет тем же самым что
future_X = foo()
#future_X можно хранить, но если он будет удалён без выполнения - это даст ошибку never awaited
X = await future_X