- Разбить корутину на части
- Настоящая сила await
- Чем закончится кроличья нора?
- await vs event loop
- await vs loop.create_task
- Попробуйте бесплатные уроки по Python
- Syntaxerror: await outside function
- What is “await” keyword?
- What is “syntaxerror: ‘await’ outside function” error message?
- Why does the “syntaxerror: await outside function” occur?
- Why can I only use the await keyword inside of async function?
- How to fix the “syntaxerror: await outside function”?
- Use the asyncio.run() method to call the async function or method
- Conclusion
- Leave a Comment Cancel reply
Разбить корутину на части
Программа, в которой кода много, а функций совсем нет — это нечитаемая простыня write-only кода. В норме программа состоит из большого числа функций, и большинство из них являются надстройками: первая запускает вторую, а та — третью и четвертую. Если с синхронными функциями все просто: поставили скобки, передали аргументы и вот он результат —, то что делать с асинхронным кодом? Ведь вместо результата асинхронная функция возвращает объект корутины. Как вызвать асинхронную функцию из другой асинхронной функции?
Настоящая сила await
Рассмотрим пример с часами в консоли. Это таймер, который отсчитывает секунды и в конце подает звуковой сигнал:
import asyncio async def run_timer(secs): for secs_left in range(secs, 0, -1): print(f'Осталось secs_left> сек.') await asyncio.sleep(1) print('Время вышло!') print('\a') # says beep with ASCII BEL symbol coroutine = run_timer(3) asyncio.run(coroutine) # requires python 3.7
Корутину coroutine запускает функция asyncio.run — внутри она вызывает метод coroutine.send(…) и делает секундные паузы, ориентируюсь по await asyncio.sleep(1) . Эти две функции — run и sleep — тесно связаны между собой, работают в паре, но не по отдельности. Исключение из правил — это asyncio.sleep(0) с нулем в качестве аргумента. Она похожа на пустое значение None и не требует для своей работы asyncio.run(…) . Этой особенностью мы воспользовались во вводной статье про корутины.
Вернемся к программе. Она отсчитает 3 секунды, и затем завершит свою работу:
Осталось 3 сек. Осталось 2 сек. Осталось 1 сек. Время вышло!
Теперь научим программу отсчитывать минуты. Пока времени остается много, пользователю не важны секунды, ему достаточно отсчета минут:
import asyncio async def run_five_minutes_timer(): for minutes_left in range(5, 1, -1): print(f'Осталось minutes_left> мин.') await asyncio.sleep(60) # one minute # TODO добавить отсчет секунд, как в async def run_timer print('Время вышло!') print('\a') # says beep with ASCII BEL symbol coroutine = run_five_minutes_timer() asyncio.run(coroutine)
Этот таймер отсчитает пять минут. Первые четыре минуты он спит по 60 секунд, а затем переключается на секундный отсчет. По крайней мере, так задумано, но как это сделать без копирования кода?
И тут на сцену выходит await . Он умеет работать не только с asyncio.sleep , но и с любой другой асинхронной функцией. Код нашего таймера теперь выглядит так:
import asyncio async def countdown_minutes_till_one_left(minutes): for minutes_left in range(minutes, 1, -1): print(f'Осталось minutes_left> мин.') await asyncio.sleep(60) # one minute print(f'Осталась 1 мин.') async def countdown_seconds(secs): for secs_left in range(secs, 0, -1): print(f'Осталось secs_left> сек.') await asyncio.sleep(1) async def run_five_minutes_timer(): await countdown_minutes_till_one_left(5) await countdown_seconds(60) print('Время вышло!') print('\a') # says beep with ASCII BEL symbol coroutine = run_five_minutes_timer() asyncio.run(coroutine)
Все самое интересное происходит внутри async def run_five_minutes_timer . Эта асинхронная функция сразу же передает управление в корутину countdown_minutes_till_one_left(5) и ждет её завершения, пока та не истощится. Затем то же происходит с отсчетом секунд: управление передается в countdown_seconds(60) . В итоге, до первого вызова print(‘Время вышло!’) программа добирается целых 5 минут времени:
Осталось 5 мин. Осталось 4 мин. Осталось 3 мин. Осталось 2 мин. Осталось 1 мин. Осталось 60 сек. Осталось 59 сек. … Осталось 3 сек. Осталось 2 сек. Осталось 1 сек. Время вышло!
Команду await можно использовать с любой корутиной. Но обязательно помещайте await внутрь асинхронной функции:
async def countdown_seconds(secs): # TODO do stuff await countdown_seconds(60) # SyntaxError: 'await' outside function
Команда await даёт нам мощный инструмент декомпозиции. С ней корутины можно вызывать так же, как обычные функции, достаточно поставить await . Возможно собрать цепочку вызовов, в которой одна корутина авейтит вторую, а та — третью и так до бесконечности.
Чем закончится кроличья нора?
В примере выше цепочка корутин непременно заканчивалась вызовом asynio.sleep(…) — это тоже корутина, но как она реализована внутри? В ней тоже есть await с еще одной корутиной ?
Да, внутри asyncio.sleep тоже есть await , но авейтит он не корутину. Инструкция await умеет работать и с другими объектами. Всех вместе их называют awaitable-объектами.
Подобно тому, как функция str(my_obj) перепоручает работу методу объекта my_obj.__str__() , так же и await my_obj передает управление методу my_obj.__await__() . Все объекты, у которых есть метод __await__ , называют awaitable-объектами, и все они совместимы с await . Библиотека asyncio предоставляет большой набор таких объектов: Future, Task, Event и пр. В этой статье мы не будем вдаваться в детали, это предмет отдельной статьи. Если нужны подробности — читайте документацию по asyncio.
Любая цепочка корутин заканчивается вызовом await для некорутины
await vs event loop
В программе может быть только один event loop, но много await . Возьмем функцию с таймером и запустим сразу три корутины:
loop = asyncio.get_event_loop() # просим event loop работать с тремя корутинами loop.create_task(run_five_minutes_timer()) loop.create_task(run_five_minutes_timer()) loop.create_task(run_five_minutes_timer()) # запускаем evet loop, оживают корутины loop.run_forever()
В этой программе один event loop и он находится внутри метода loop.run_forever() . Event loop контролирует исполнение сразу трех корутин — таймеров, по-очереди вызывая их методы coroutine.send() . Так он создает иллюзию их параллельного исполнения:
Осталось 5 мин. Осталось 5 мин. Осталось 5 мин. Осталось 4 мин. Осталось 4 мин. Осталось 4 мин. …
Удивительно, но хотя первая же строка кода функции run_five_minutes_timer передает управление в другую корутину с помощью await countdown_minutes_till_one_left(5) , это не мешает работе других таймеров. Они словно не замечают, что первый таймер “завис” и ждет исчерпания корутины countdown_minutes_till_one_left(5) .
Неверно воспринимать await как маленький самостоятельный event loop. Команда await больше похожа на посредника, который помогает подняться по стеку вызовов корутин и достучаться до верхнего event loop.
Оживляет корутины только event loop, await ему помогает
await vs loop.create_task
С точки зрения той корутины, которую запускают, особой разницы нет. Оба варианта сработают:
async def run_first_function(): await run_five_minutes_timer() ...
async def run_second_function(): loop.create_task(run_five_minutes_timer()) ...
В обоих случаях корутина run_five_minutes_timer() запустится и начнет отсчет времени с минутными интервалом. Однако, большая разница есть для внешних функций: run_first_function и run_second_function .
Первая — run_first_function — не сможет продолжить работу прежде, чем завершится run_five_minutes_timer() . Команда await будет исправно возвращать управление в event loop, но не в функцию run_first_function .
Вторая — run_second_function — продолжит свое выполнение сразу после вызова loop.create_task() . Корутина run_five_minutes_timer() будет добавлена в event loop и начнет свою работу, но функция run_second_function не будет её ждать.
Корутина где есть await блокируется до исчерпания вложенной корутины
На этом различия между await и loop.create_task не заканчиваются, а только начинаются. Они возвращают разные результаты, они по-разному обрабатывают исключения, они могут принимать разные аргументы. Эта тема тянет на отдельную статью.
Попробуйте бесплатные уроки по Python
Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.
Переходите на страницу учебных модулей «Девмана» и выбирайте тему.
Syntaxerror: await outside function
Encountering the syntaxerror: await outside function error messages are inevitable.
Sometimes, this error message it’s quite frustrating, especially, if you don’t know how to fix it.
Fortunately, in this article, we’ll explore how to fix this error, and you’ll know why await is only valid in async functions .
What is “await” keyword?
The “await” keyword is used in a type of programming called asynchronous programming. It’s used when we want different tasks to run at the same time, which helps the computer use its resources more efficiently.
But remember, you can only use the “await” keyword inside a special kind of function called an asynchronous function.
What is “syntaxerror: ‘await’ outside function” error message?
The error message syntaxerror: ‘await’ outside function occurs when we are trying to use an await keyword outside of an async function or method.
Await is used in an async function or method to wait on other asynchronous tasks.
For example:
import asyncio async def my_async_function(): await asyncio.sleep(1) return "Hello, Welcome to Itsourcecode!" result = await my_async_function() print(result)
File "C:\Users\pies-pc2\PycharmProjects\pythonProject\main.py", line 7 result = await my_async_function() ^^^^^^^^^^^^^^^^^^^^^^^^^ SyntaxError: 'await' outside function
Therefore, if you are calling the “await” keyword outside of an async function or method, SyntaxError will arise.
So, in simple words, this error typically occurs when the await keyword is used outside of an asynchronous function.
Why does the “syntaxerror: await outside function” occur?
This error message occurs due to several reasons, such as:
This error message occurs due to several reasons, such as:
❌Using await in a Synchronous Function
When using await, it is essential to remember that it can only be used within asynchronous functions. If you mistakenly use await in a synchronous function, the interpreter will raise SyntaxError.
❌ Misplaced await Statement
Placing the await keyword outside of any function. In Python, every awaits statement should be enclosed within an asynchronous function.
❌ Using await in a Regular Block
Await keyword cannot be used in regular blocks such as if statements or loops. The error will be raised if you try to use it in such contexts.
Why can I only use the await keyword inside of async function?
The reason you can only use the await keyword inside of an async function because it ensures proper flow control and synchronization between asynchronous tasks.
If you mark a function as async and use the await keyword within it, you indicate to the interpreter that the function contains asynchronous operations and should be treated accordingly.
How to fix the “syntaxerror: await outside function”?
To fix the syntaxerror ‘await’ outside function , ensure that the await keyword is used inside an async function or method.
If you are calling an async function or method outside of an async function or method, you need to use the asyncio.run() method to call the async function or method.
Use the asyncio.run() method to call the async function or method
Incorrect code:
❌ import asyncio async def my_async_function(): await asyncio.sleep(1) return "Hello, Welcome to Itsourcecode!" result = await my_async_function() print(result)
Corrected code:
✅ import asyncio async def my_async_function(): await asyncio.sleep(1) return "Hello, Welcome to Itsourcecode!" result = asyncio.run(my_async_function()) print(result)
Hello, Welcome to Itsourcecode!
Conclusion
In conclusion, the error message syntaxerror: ‘await’ outside function occurs when we are trying to use an await keyword outside of an async function or method.
Await is used in an async function or method to wait on other asynchronous tasks.
To fix this error, ensure that the await keyword is used inside an async function or method.
This article already discussed what this error is all about and multiple ways to resolve this error.
By executing the solutions above, you can master this SyntaxError with the help of this guide.
You could also check out other SyntaxError articles that may help you in the future if you encounter them.
We are hoping that this article helps you fix the error. Thank you for reading itsourcecoders 😊
Leave a Comment Cancel reply
You must be logged in to post a comment.