源码索引 / CURRENT IMPLEMENTATION
learning/go-patterns/stages/04-context/main.go
89 行 · 点击行号可定位;函数链接随声明位置更新,不手填行号。
跳到函数 / 声明
// Stage 04 carries context through the service and source so waiting can stop.
// Run it with: go run .
package main
import (
"context"
"errors"
"fmt"
"time"
)
var ErrUnknownItem = errors.New("unknown item")
type Price struct {
Item string
Cents int
}
type PriceSource interface {
Lookup(context.Context, string) (Price, error)
}
type Catalog map[string]Price
type DelayedCatalog struct {
prices Catalog
delay time.Duration
}
func NewDelayedCatalog(prices Catalog, delay time.Duration) DelayedCatalog {
return DelayedCatalog{prices: prices, delay: delay}
}
// Lookup waits for the source delay, but also wakes when the caller cancels.
func (c DelayedCatalog) Lookup(ctx context.Context, item string) (Price, error) {
if err := ctx.Err(); err != nil {
return Price{}, err
}
price, ok := c.prices[item]
if !ok {
return Price{}, fmt.Errorf("%w: %q", ErrUnknownItem, item)
}
if c.delay <= 0 {
return price, nil
}
timer := time.NewTimer(c.delay)
defer timer.Stop()
select {
case <-timer.C:
return price, nil
case <-ctx.Done():
return Price{}, ctx.Err()
}
}
type Service struct {
source PriceSource
}
func NewService(source PriceSource) Service {
return Service{source: source}
}
func (s Service) Quote(ctx context.Context, item string) (Price, error) {
price, err := s.source.Lookup(ctx, item)
if err != nil {
return Price{}, fmt.Errorf("quote %q: %w", item, err)
}
return price, nil
}
func main() {
service := NewService(NewDelayedCatalog(
Catalog{"book": {Item: "book", Cents: 2100}},
5*time.Millisecond,
))
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
price, err := service.Quote(ctx, "book")
if err != nil {
fmt.Println("quote failed:", err)
return
}
fmt.Printf("%s: %d cents\n", price.Item, price.Cents)
}