非同期処理とは、処理が終わるのを待たずに次の処理を進める仕組みのことで、特にDiscord Botのように外部APIと通信する際に重要になります
サンプルコード
async と await を使った処理の順番がわかるシンプルなサンプルコードを紹介します
このコードでは、async / await を使って処理の順番を確認できるようにします
import asyncio
async def task1():
print("Task 1: 開始")
await asyncio.sleep(2) # 2秒待機(非同期)
print("Task 1: 終了")
async def task2():
print("Task 2: 開始")
await asyncio.sleep(1) # 1秒待機(非同期)
print("Task 2: 終了")
async def main():
print("Main: 開始")
# それぞれのタスクを同時に実行
await asyncio.gather(task1(), task2())
print("Main: 終了")
# 非同期処理の実行
asyncio.run(main())
実行結果(出力順)
Main: 開始
Task 1: 開始
Task 2: 開始
Task 2: 終了
Task 1: 終了
Main: 終了
処理の流れ
- main() が呼ばれる
- print(“Main: 開始”) が実行される
- asyncio.gather(task1(), task2()) により task1() と task2() が 同時に実行 される
- task1() の “Task 1: 開始” が出力される
- task2(
)
の “Task 2: 開始” が出力される
- task2() は await asyncio.sleep(1) で 1秒待機
- task1() は await asyncio.sleep(2) で 2秒待機
- 1秒経過後、task2() が再開し “Task 2: 終了” を出力
- さらに1秒経過後(合計2秒経過)、task1() が再開し “Task 1: 終了” を出力
- asyncio.gather() の処理がすべて終わり、main() の “Main: 終了” が出力される
ポイント
- await asyncio.sleep(n) は 処理を中断してn秒待つ(その間、他のタスクが動ける)。
- asyncio.gather(task1(), task2()) で 複数のタスクを並行実行 できる。
- task2() は 1秒で終わるので、task1() より先に “Task 2: 終了” が出る。
まとめ
async は 「asynchronous(非同期)」 の略です。
Python では、async def を使うことで「非同期関数(コルーチン)」を定義できます
非同期(asynchronous)とは、「処理が終わるのを待たずに次の処理を進める」仕組みのことです
- await は 非同期処理の完了を待つ キーワード(async なしでは await は使えない)
つまり
- async = 「非同期の」
- await = 「非同期処理の完了を待つ」
という意味になります
Discord botでの活用例
以前の記事で紹介した株価ローソク足チャートを返すDiscord botの関数でasyncを使用しているので解説します
以下にDiscord botのコードの一部を書きます
コード全体は以前の記事を確認してください
# スラッシュコマンドの登録
@tree.command(name="ticker", description="指定した銘柄の株価チャートを取得")
async def ticker(interaction: discord.Interaction, symbol: str):
await interaction.response.defer() # 処理中の表示
buf = create_candlestick_chart(symbol)
if buf:
await interaction.followup.send(file=discord.File(buf, filename=f"{symbol}_chart.png"))
else:
await interaction.followup.send("⚠️ チャートの生成に失敗しました。ティッカーを確認してください。")
# Botが起動したときにスラッシュコマンドを同期
@bot.event
async def on_ready():
await tree.sync()
print(f"Logged in as {bot.user}")
1. async def で非同期関数を定義
async def ticker(interaction: discord.Interaction, symbol: str):
この関数は 非同期関数 になっています
非同期関数 (async def) は、通常の関数 (def) と異なり、処理を中断 (await) しながら進めることができます
2. await で非同期処理の完了を待つ
await interaction.response.defer() # 処理中の表示
これは、Discordのスラッシュコマンドのレスポンスを「処理中」の状態にするための非同期処理です
await を使うことで、この処理が完了するまで待つことができます
(もし await を付けずに interaction.response.defer() を実行すると、関数の実行が終わる前に次の処理に進んでしまい、エラーの原因になります)
await interaction.followup.send(file=discord.File(buf, filename=f"{symbol}_chart.png"))
これも同じく await を使って、Discordへの画像送信が完了するまで待つようにしています
3. on_ready() の await
@bot.event
async def on_ready():
await tree.sync()
print(f"Logged in as {bot.user}")
この await tree.sync() も非同期処理で、Botが起動したときにスラッシュコマンドを同期するためのものです
これが完了するのを待たずに print() を実行すると、コマンドの登録が終わる前にBotが動き出してしまうため、適切に await を使って処理の順番を守っています
まとめ
- async def で関数を非同期化することで、await を使った非同期処理が可能になる
- await は、時間のかかる処理(Discord APIへのリクエストなど)が終わるまで待機するために使う
- await を適切に使わないと、処理の順番が崩れてエラーが発生しやすくなる
このようにDiscordのAPI通信 (interaction.response.defer() や interaction.followup.send()) など、外部サービスとのやり取りが含まれているため、async / await を適切に使うことが重要になっています
コメント