// Debounce wraps e, preventing duplicate NamedActions from running // concurrently, even from concurrent calls to Execute. func Debounce(e Executor) Executor { return debouncer{ ex: e, sf: new(singleflight.Group), } } type debouncer struct { ex Executor sf *singleflight.Group } // For any action func (d debouncer) Execute(ctx context.Context, actions []Action) error { wrapped := make([]Action, len(actions)) for i, a := range actions { if na, ok := a.(NamedAction); ok { wrapped[i] = debouncedAction{ NamedAction: na, sf: d.sf, } } else { wrapped[i] = actions[i] } } return d.ex.Execute(ctx, wrapped) } type debouncedAction struct { NamedAction sf *singleflight.Group } func (da debouncedAction) Execute(ctx context.Context) error { fn := func() (interface{}, error) { return nil, da.NamedAction.Execute(ctx) } _, err, _ := da.sf.Do(da.ID(), fn) return err }