Files
sanic/guide/content/en/guide/basics/tasks.md
2023-09-06 15:44:00 +03:00

136 lines
3.8 KiB
Markdown

# Background tasks
## Creating Tasks
It is often desirable and very convenient to make usage of [tasks](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task) in async Python. Sanic provides a convenient method to add tasks to the currently **running** loop. It is somewhat similar to `asyncio.create_task`. For adding tasks before the 'App' loop is running, see next section.
```python
async def notify_server_started_after_five_seconds():
await asyncio.sleep(5)
print('Server successfully started!')
app.add_task(notify_server_started_after_five_seconds())
```
.. column::
Sanic will attempt to automatically inject the app, passing it as an argument to the task.
.. column::
```python
async def auto_inject(app):
await asyncio.sleep(5)
print(app.name)
app.add_task(auto_inject)
```
.. column::
Or you can pass the `app` argument explicitly.
.. column::
```python
async def explicit_inject(app):
await asyncio.sleep(5)
print(app.name)
app.add_task(explicit_inject(app))
```
## Adding tasks before `app.run`
It is possible to add background tasks before the App is run ie. before `app.run`. To add a task before the App is run, it is recommended to not pass the coroutine object (ie. one created by calling the `async` callable), but instead just pass the callable and Sanic will create the coroutine object on **each worker**. Note: the tasks that are added such are run as `before_server_start` jobs and thus run on every worker (and not in the main process). This has certain consequences, please read [this comment](https://github.com/sanic-org/sanic/issues/2139#issuecomment-868993668) on [this issue](https://github.com/sanic-org/sanic/issues/2139) for further details.
To add work on the main process, consider adding work to [`@app.main_process_start`](./listeners.md). Note: the workers won't start until this work is completed.
.. column::
Example to add a task before `app.run`
.. column::
```python
async def slow_work():
...
async def even_slower(num):
...
app = Sanic(...)
app.add_task(slow_work) # Note: we are passing the callable and not coroutine object ...
app.add_task(even_slower(10)) # ... or we can call the function and pass the coroutine.
app.run(...)
```
## Named tasks
.. column::
When creating a task, you can ask Sanic to keep track of it for you by providing a `name`.
.. column::
```python
app.add_task(slow_work, name="slow_task")
```
.. column::
You can now retrieve that task instance from anywhere in your application using `get_task`.
.. column::
```python
task = app.get_task("slow_task")
```
.. column::
If that task needs to be cancelled, you can do that with `cancel_task`. Make sure that you `await` it.
.. column::
```python
await app.cancel_task("slow_task")
```
.. column::
All registered tasks can be found in the `app.tasks` property. To prevent cancelled tasks from filling up, you may want to run `app.purge_tasks` that will clear out any completed or cancelled tasks.
.. column::
```python
app.purge_tasks()
```
This pattern can be particularly useful with `websockets`:
```python
async def receiver(ws):
while True:
message = await ws.recv()
if not message:
break
print(f"Received: {message}")
@app.websocket("/feed")
async def feed(request, ws):
task_name = f"receiver:{request.id}"
request.app.add_task(receiver(ws), name=task_name)
try:
while True:
await request.app.event("my.custom.event")
await ws.send("A message")
finally:
# When the websocket closes, let's cleanup the task
await request.app.cancel_task(task_name)
request.app.purge_tasks()
```
*Added in v21.12*