trioによる並行処理③（move_on_after, move_on_at）

[目次](https://qiita.com/Kyosuke_Ichikawa/items/b7fdffcbfe00773074a6)

trioにある便利な関数[move_on_after()](https://trio.readthedocs.io/en/stable/reference-core.html#trio.move_on_after)と[move_on_at()](https://trio.readthedocs.io/en/stable/reference-core.html#trio.move_on_at)を紹介します。

## move_on_after
```with move_on_after(秒数)```のようにしてwithブロックを作ることができ、指定した秒数経つとブロックを抜けます。例えば
```python
import trio

async def main():
    start_time = trio.current_time()
    with trio.move_on_after(3):
        while True:
            elapsed_time = trio.current_time() - start_time
            print(f"elapsed_time={elapsed_time:.1f}")
            await trio.sleep(1)
    elapsed_time = trio.current_time() - start_time
    print(f"done (elapsed_time={elapsed_time:.1f})")

trio.run(main)
```
```:結果
elapsed_time=0.0
elapsed_time=1.0
elapsed_time=2.0
done (elapsed_time=3.0)
```

- ```trio.current_time()```は現在の時間を秒単位で取得する関数です
- ```while True```のループは中で```await trio.sleep(1)```を使っているため1秒周期で回る無限ループです
  - 無限ループですが```with trio.move_on_after(3)```のブロック内にあるため3秒経ったら抜けます
  - 抜けるのは```await```で非同期関数が呼ばれたタイミングです（ここでは```await trio.sleep(1)```）

尚、timeoutで抜けたかどうかは```move_on_after()```の返り値の[CancelScope](https://trio.readthedocs.io/en/stable/reference-core.html#trio.CancelScope)のcancelled_caughtで分かります。
timeoutで抜けた場合はTrueとなります。例えば

```python
import trio

async def main():
    with trio.move_on_after(3) as cancel_scope:
        for _ in range(5):
            await trio.sleep(1)
    print(f"cancelled_caught={cancel_scope.cancelled_caught}")

trio.run(main)
```
```:実行結果
cancelled_caught=True
```
ここで```move_on_after()```の引数を6にするとタイムアウトで抜けなくなり、実行結果は```cancelled_caught=False```となります。


## move_on_at
```with move_on_at(秒数)```のようにwithブロックを作ることができます。
```move_on_after()```はtimeoutまでの秒数を指定する関数でしたが、```move_on_at()```はtimeoutの時刻を指定します。例えば
```python
import trio

async def main():
    start_time = trio.current_time()
    deadline = start_time + 3
    with trio.move_on_at(deadline):
        while True:
            elapsed_time = trio.current_time() - start_time
            print(f"elapsed_time={elapsed_time:.1f}")
            await trio.sleep(1)
    elapsed_time = trio.current_time() - start_time
    print(f"done (elapsed_time={elapsed_time:.1f})")

trio.run(main)
```
```:結果
elapsed_time=0.0
elapsed_time=1.0
elapsed_time=2.0
done (elapsed_time=3.0)
```
尚、timeoutの時刻（deadline）は動的に変えることができます。
```move_on_at()```の返り値の[CancelScope](https://trio.readthedocs.io/en/stable/reference-core.html#trio.CancelScope)のdeadlineを更新すればよいです。
上の例のtimeoutを3秒ではなく5秒に変更してみます。

```python
import trio

async def main():
    start_time = trio.current_time()
    deadline = start_time + 3
    with trio.move_on_at(deadline) as cancel_scope:
        cancel_scope.deadline = start_time + 5
        while True:
            elapsed_time = trio.current_time() - start_time
            print(f"elapsed_time={elapsed_time:.1f}")
            await trio.sleep(1)
    elapsed_time = trio.current_time() - start_time
    print(f"done (elapsed_time={elapsed_time:.1f})")

trio.run(main)
```
```:結果
elapsed_time=0.0
elapsed_time=1.0
elapsed_time=2.0
elapsed_time=3.0
elapsed_time=4.0
done (elapsed_time=5.0)
```
また、```move_on_after()```と同様に```CancelScope.cancelled_caught```で、timeoutしたかどうかが分かります。