Flask 2.0 から async/await がサポートされたが、制限や落とし穴がある。Flask で非同期処理を扱う際の注意点と回避策を解説する。
Flask 2.0 の async サポート
Flask 2.0 以降では、ビュー関数を async def で定義できる。
from flask import Flask app = Flask(__name__) @app.route('/async') async def async_view(): await some_async_operation() return 'Done'
ただし、Flask 自体は WSGI フレームワークであり、真の非同期フレームワーク(FastAPI など)とは異なる。
制限: 同期的なコンテキスト
Flask のリクエストコンテキストは同期的に設計されている。非同期タスクを別スレッドやコルーチンで実行すると、コンテキストが伝播しない。
import asyncio @app.route('/problem') async def problem(): # 危険: 別タスクではコンテキストが存在しない task = asyncio.create_task(background_work()) await task return 'Done' async def background_work(): # RuntimeError: Working outside of application context print(current_app.name)
回避策 1: copy_current_request_context
Flask が提供するデコレータでコンテキストをコピーできる。
from flask import copy_current_request_context @app.route('/safe') async def safe(): @copy_current_request_context async def background_work(): # コンテキストがコピーされている return current_app.name result = await background_work() return result
回避策 2: 必要な値を先に取得
コンテキストに依存するデータを先に取り出してから非同期処理に渡す。
@app.route('/extract') async def extract(): # 先に必要な値を取得 config_value = current_app.config['API_KEY'] user_id = g.user.id # 非同期処理には値だけを渡す result = await process_async(config_value, user_id) return result
非同期 WSGI サーバー
Flask で async を最大限活用するには、非同期対応の WSGI サーバーを使う。
pip install asgiref gunicorn -k uvicorn.workers.UvicornWorker app:app
または Hypercorn を使う。
pip install hypercorn hypercorn app:app
Flask vs 真の非同期フレームワーク
| Flask | WSGI ベース、async は後付けサポート |
| FastAPI | ASGI ネイティブ、非同期が第一級市民 |
| Quart | Flask 互換の ASGI フレームワーク |
大量の I/O バウンドな処理が必要なら、Quart への移行も検討すべきである。Quart は Flask とほぼ同じ API を持つ。
from quart import Quart app = Quart(__name__) @app.route('/async') async def async_view(): # Quart では自然に非同期が使える result = await async_db_query() return result
Flask での非同期は可能だが、制限を理解した上で使うこと。本格的な非同期が必要なら、ASGI フレームワークを検討すべきである。