asyncとscope
タイマーやDBなどの処理を待つ関数はasync defで定義し、awaitで結果を待ちます。待っている間は、ほかの非同期処理を進められます。
async def main() -> Result[unit, Error]:
await sleep(10)
return ok(print("待ち終わりました"))
sleepの引数はミリ秒です。上のコードは待ち終わってからメッセージを表示します。失敗する可能性のある処理では、try await db_open(...)のように結果のエラーも扱います。
このページの目次
複数の処理を始める
async with scopeの中でspawnすると、子の処理を始められます。scopeを出るときに、すべての子の終了を待ちます。
async def main() -> Result[unit, Error]:
async with scope:
spawn sleep(10)
spawn sleep(15)
return ok(print("完了"))
scope本体が終わると、子の結果を確認します。子がResultのエラーを返したりpanicしたりすると、残りをキャンセルして終了を待ちます。scope本体の実行中に子の失敗で割り込む動作はありません。spawnできるのは、unitかResult[unit, Error]を返す非同期処理です。scope内のreturnと、viewを子へ渡すことは未対応です。
引数はspawnを書いた場所で評価し、できた値を子へ渡します。spawn work(copy(part))のようにviewから所有値を作ると、元のデータを親でも使い続けられます。配列をコピーしても中身にviewが残る場合は、子へ渡せません。
scopeを使う関数はResult[T, Error]を返します。独自のエラーclassを使う場合は、Rust連携でFrom<nagi_runtime::Error>を実装してください。子の失敗をそのclassへ変換できることはビルド時に確認します。
親の処理そのものが破棄された場合や、scope本体がpanicした場合には、子へ停止を要求します。その場で全員の終了を待つ保証はありません。CPU処理の停止については並行処理を参照してください。
関数を変数に入れて呼び出す
async関数も、関数名を変数へ代入して呼び出せます。次の例は42を表示します。
async def answer(value: i64) -> i64:
return value + 1
async def main():
selected = answer
print(await selected(41))
これは関数そのものの代入です。pending = answer(41)のように呼び出した戻り値を保存する形式は未対応です。呼び出しとawaitを合わせて書いてください。関数を引数や戻り値として受け渡す型の範囲は型と推論にあります。
async関数を入れた変数に、別のasync関数を再代入することはできません。別の変数を使うか、ifの各分岐で呼び出してください。同じ関数を入れ直すことや、同期関数を入れた変数の差し替えはできます。