【Python初心者】非同期処理とは何か？実務での使い方までをわかりやすく解説

Pythonで `async` や `await` を見たことはあるけれど、使いどころがわからなかったり、ピンとこなかったりする方も多いと思います。  
この記事では、**非同期処理とは何か**、そして**どんな場面で活用できるのか**を、実務の例を交えながら初心者にもわかるように解説します。

## 🚦 非同期処理とは「終わるのを待たずに、次を始められる」こと

例えば、**Web API** からデータを取得するときの処理を考えてみましょう。

### ✅ 同期処理なら…

```python
import requests

def main():
    urls = ["A", "B", "C"]
    for url in urls:
        res = requests.get(url)
        print(res.text)
```

- このコードでは、**1つのリクエストが終わるまで次のリクエストが始まりません**。
- 各リクエストを1つずつ順番に処理します。


### ✅ 非同期処理なら…

```python
import asyncio

async def fetch_data(url):
    print(f"Fetching {url}")
    await asyncio.sleep(2)
    print(f"Done with {url}")

async def main():
    urls = ["A", "B", "C"]
    tasks = [fetch_data(url) for url in urls]
    await asyncio.gather(*tasks)

asyncio.run(main())
```

- 非同期処理を使うと、各リクエストを**同時に並行して処理**することができます。
- **1つのリクエストを待っている間にも、他のリクエストを進行できる**ため、全体の待ち時間を短縮できます。


## 🛠 非同期処理に必要な要素の例

| 構文               | 意味                                                               |
|--|--|
| `async def`        | 非同期関数を定義する                                               |
| `await`            | 非同期関数の中で「待つ場所」                                       |
| `asyncio`          | Python標準の非同期処理を行うためのモジュール（イベントループなどを提供） |
| `asyncio.gather()` | 複数の非同期処理を同時に走らせる                                   |
| `aiohttp`          | 非同期でHTTP通信を行うためのライブラリ（実務向け）                |

## 🔰 非同期処理の基本構成コード

```python
import asyncio

async def say_hello():
    print("Hello")
    await asyncio.sleep(1)
    print("World")

async def main():
    await say_hello()

asyncio.run(main())
```

### 🔹 awaitについて

`await` は「その場で待つ」というより、**「その処理が終わるのを待つあいだ、他の処理に制御を譲る」**という意味です。

非同期処理の世界では、「順番待ちをしている間に他の仕事を進める」ことが基本の考え方です。


## 🧪 実務例：APIを非同期で並列取得

実務では、複数のAPIを並行して処理したい場面がよくあります。  
例えば、商品情報・在庫情報・レビュー情報などをそれぞれのAPIから取得するとき、同期処理だと1件ずつしか処理できませんが、非同期処理を使えば全てのリクエストを**同時に処理**できます。

```python
import asyncio
import aiohttp

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    urls = [
        "https://httpbin.org/delay/1",
        "https://httpbin.org/delay/2",
        "https://httpbin.org/delay/3"
    ]

    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        results = await asyncio.gather(*tasks)

    for res in results:
        print(res[:60], "...")  # レスポンスが長いため、先頭60文字だけ表示

asyncio.run(main())
```
### 🔹 asyncio.gather()について

複数の非同期処理をまとめて同時に実行する関数です。  
引数に渡したすべてのタスクが完了するのを待ち、結果をまとめて返します。

たとえば：

```python
await asyncio.gather(task1(), task2(), task3())
```

このように書くと、3つの処理が**同時に実行**され、それぞれの `await` が終わった順に結果が返ってきます。
### 🔹 async with とは？

非同期バージョンです。  
**「非同期的にリソースを開いて、使い終わったら非同期的に閉じる」**ときに使います。

#### ✔ よく使う場面
- 非同期な接続処理（HTTP通信、DB接続など）
- 非同期なファイル・ストリーム処理


## 🔁 全体の処理の流れ

1. `main()` 関数が実行される  
2. 非同期セッションを開始（`async with aiohttp.ClientSession()`）  
3. 3つの `fetch()` 関数を作成し、非同期に同時実行（`asyncio.gather`）  
4. 各 `fetch()` 内で `async with session.get(url)` によりリクエスト送信  
5. 各レスポンスを `await response.text()` で取得  
6. 全レスポンスを順に表示

### ✅ ポイント解説

- `fetch()` 関数は非同期のAPI呼び出し関数です。
- `asyncio.gather()` により、全てのリクエストが**並行して実行**されます。
- 各リクエストが**順番ではなく同時に進行する**ので、全体の処理時間が短縮されます。



※このコードを実行するには、`aiohttp` をインストールしておく必要があります：

```bash
pip install aiohttp
```


## 🧠 まとめ

非同期処理を使うと、**待機時間を有効活用できる**ことがわかりました。  
例えば、APIを複数同時に叩くケースなどでは、非同期処理によって全体の待機時間を短縮できます。

- `async def` で非同期関数を定義  
- `await` で待機する場所を指定  
- `asyncio` は非同期処理を行うための標準モジュール  
- `aiohttp` を使えば実務でも使える非同期HTTP通信が可能  

今回紹介した非同期処理の基本を理解した上で、少しずつ実務にも応用できるように学んでいきたいと思います。