非同期処理ついてのメモ書き
非同期処理とは、ある処理の完了を待たずに別の処理を実行できる仕組みのこと。
JavaScript における非同期処理
コールバック
昔から非同期処理といえば「コールバック関数」を使うのが一般的だった。
関数(引数, callback関数);
callback関数(エラーオブジェクト, 結果);
ただし、コールバックが入れ子になっていくと「コールバック地獄」と呼ばれる可読性の低下が起きる。
Promise / async/await
そこで Promise や async/await が導入され、次のように書けるようになった。
- Promise: 非同期処理の結果を「成功(Fulfilled)」または「失敗(Rejected)」として扱う仕組み
- async/await: 非同期処理を同期処理のように書ける糖衣構文
async function example() {
try {
const result = await fetchData();
console.log(result);
} catch (err) {
console.error(err);
}
}
Node.js は ノンブロッキングI/O を採用しており、ファイル操作・ネットワーク通信・DBアクセスなど時間のかかる処理は基本的に非同期処理で行われる。
Python における非同期処理
Python では async/await と asyncio ライブラリを使う。
async
: コルーチン関数を定義するawait
: 非同期処理が終わるまで待つasyncio.run()
: イベントループを起動してコルーチンを実行する
コルーチンはタスクとして実行され、await
で一時中断 → 処理再開が可能。
Python 3.11 以降では asyncio.TaskGroup
で複数タスクをまとめて扱える。
サンプルコード
import asyncio
import time
async def task(delay: int):
print(f'{delay}s Hello …')
await asyncio.sleep(delay)
print(f' {delay}s … World!')
async def main():
print(f"started at {time.strftime('%X')}")
await task(3)
print('task(3) の次の行')
await task(1)
print('task(1) の次の行')
# 複数同時に実行したい場合
# await asyncio.gather(task(3), task(1))
t = task(2) # await を付けないと「実行されないコルーチンオブジェクト」が返るだけ
print(f"finished at {time.strftime('%X')}")
if __name__ == '__main__':
asyncio.run(main())
並べて比較
項目 | JavaScript | Python |
---|---|---|
基盤 | ノンブロッキングI/O | asyncio(イベントループ) |
初期の方法 | コールバック | スレッド/同期I/Oが主流 |
現在の主流 | Promise / async/await | async/await + asyncio |
イベントループ | Node.jsが自動管理 | asyncio.run() で明示的に管理 |
並行実行 | Promise.all | asyncio.gather , TaskGroup |
非同期処理を使う場面(例)
- ファイルのアップロード・ダウンロード
- データベースへの読み書き
- ネットワーク越しのAPIリクエスト
- 処理に時間がかかるIO操作全般
まとめ
- JavaScript では Promise / async/await が主流
- Python では asyncio と async/await を利用
- どちらも「同期処理のように順番に書ける」点が分かりやすさのメリット
以上になります。またお会いしましょう