1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| type Subscription interface { Updates() <-chan Item Close() error }
func Subscribe(fetcher Fetcher) Subscription { s := &sub{ fetcher: fetcher, updates: make(chan Item), closing: make(chan chan error), } go s.loop() return s }
type sub struct { fetcher Fetcher updates chan Item closing chan chan error }
func (s *sub) Updates() <-chan Item { return s.updates }
func (s *sub) Close() error { errc := make(chan error) s.closing <- errc return <-errc }
func (s *sub) loop() { const maxPending = 10 type fetchResult struct { fetched []Item next time.Time err error }
var fetchDone chan fetchResult var pending []Item var next time.Time var err error var seen = make(map[string]bool)
for { var fetchDeplay time.Duration if now := time.Now(); next.After(now) { fetchDelay = next.Sub(now) } var startFetch <-chan time.Time if fetchDone == nil && len(pending) < maxPending { startFetch = time.After(fetchDelay) }
var first Item var updates chan Item if len(pending) > 0 { first = pending[0] updates = s.updates }
select { case errc := <-s.closing: errc <- err close(s.updates) return case <-startFetch: fetchDone = make(chan fetchResult, 1) go func() { fetched, next, err := s.fetcher.Fetch() fetchDone <- fetchResult{fetched, next, err} }() case result := <-fetchDone: fetchDone = nil fetched := result.fetched next, err = result.next, result.err if err != nil { next = time.Now().Add(10 * time.second) break } for _, item := range fetched { if !seen[item.GUID] { pending = append(pending, item) seen[item.GUID] = true } } case updates <- first: pending = pending[1:] } } }
|