今更ちょっとcontext.context
の意味が少し理解できた。
そもそもcontext
とかchan
を全然使っていなかったのでこりゃなんぞと思ったが、多分以下みたいなことなのであろう、というメモ。
type User struct {
Id int
Name string
}
type requestToken string
func main() {
// 1. なんかの情報を付けたコンテクストを作成する
ctx := context.WithValue(context.Background(), requestToken("network_token"), "12345678010")
// 2. そいつを使ってユーザー情報を取得する
user, err := getUser(ctx)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Printf("success: user found, name: %s\n", user.Name)
}
// 3. この関数は外部のサーバー(仮)からユーザー情報を取得する
func getUser(ctx context.Context) (*User, error) {
// 4. コンテクストを派生させ、タイムアウトを設定する
ctx, cancel := context.WithTimeout(ctx, 6*time.Second)
defer cancel()
resp := make(chan struct {
user *User
err error
})
go func() {
user, err := getUserFromAnotherNetwork(ctx)
resp <- struct {
user *User
err error
}{user: user, err: err}
close(resp)
}()
// 5. ここは当然同期関数なので無限ループで止まる
for {
select {
case <-ctx.Done():
return nil, errors.New("request timeout")
case data := <-resp:
return data.user, data.err
}
}
}
func getUserFromAnotherNetwork(ctx context.Context) (*User, error) {
// 6. ctxの中身見て、OK?
if token, ok := ctx.Value(requestToken("network_token")).(string); !ok || token != "12345678010" {
return nil, errors.New("not authorized")
}
time.Sleep(7 * time.Second)
return &User{Id: 1, Name: "John"}, nil
}
私の理解では、詰まる所contextとは(有限な時間がある現実世界での)行動状態の抽象化であり、その状態(条件)は随時派生させることができる。時にそれは取りやめることができ、あるいは締め切りを定めることができる。そして、Done()
は終わったことを通知するインスタンスにおける一意のチャネルを生成し、それによって別の場所では待機することも出来る。
で、基本的に色々なところの第1引数にいれるのがお約束?らしい。知らないことばかりだなあ。