📑

go文を使わずにctx.Done()で中断させる

2023/10/29に公開

小ネタなのですがループ処理が行われている最中にctx.Done()になったら、 その処理を終了させるという場面があります。

サンプルコードでこういう感じのを見かける気がします(主観)。私もこんな雰囲気のものを書いたことがありました。

func ctxLoop(ctx context.Context) []string {
	out := make(chan string,100)
	go func() {
		defer close(out)
		for i := 0; i < 100; i++ {
			res := getDataFromServer()
			out <- res
		}
	}()

	var result []string
	for {
		select {
		case <-ctx.Done():
			return result
		case res, ok := <-out:
			if !ok {
				return result
			}
			result = append(result, res)
		}
	}
}

しかしあるとき気がついたのですが、こう書いてしまえば非常にシンプルになります。


func ctxLoopSimple(ctx context.Context) []string {
	var result []string
	for i := 0; i < 100; i++ {
		select {
		case <-ctx.Done():
			return result
		default:
		}

		res := getDataFromServer()
		result = append(result, res)
	}
	return result
}

chanelのハンドリングはしているのですが、実はgo文すら必要ありませんでした。

ポイントは以下の部分ですね。 ctx.Done() でなければ defaultで進行します。goroutineの無いところでもイディオム的に使っていけます。

		select {
		case <-ctx.Done():
			return result
		default:
		}

go文がなくなってコードの複雑度も下がり、バグが予防できますし、簡単です。ご参考に。

Discussion