paralleldispatcher.go
func (d *ParallelDispatcher) Dispatch(cmd interface{}) (err error) {
	d.mutex.RLock()
	defer d.mutex.RUnlock()

	defer func() {
		if e := recover(); e != nil {
			err = e.(error)
		}
	}()

	var found int32
	wg := &sync.WaitGroup{}
	errCh := make(chan error, len(d.handlers))
	for _, handler := range d.handlers {
		wg.Add(1)
		go d.dispatch(wg, errCh, &found, handler, cmd)
	}

	wg.Wait()
	close(errCh)

	if found != 1 {
		return &NoHandlerFoundError{
			Command: cmd,
		}
	}

	errs := []error{}
	for {
		e, ok := <-errCh
		if !ok {
			break
		}
		if e == nil {
			continue
		}
		errs = append(errs, e)
	}

	if len(errs) == 0 {
		return
	}

	err = errorgroup.New(errs)

	return
}
func (d *ParallelDispatcher) Dispatch(cmd interface{}) (err error) {
	d.mutex.RLock()
	defer d.mutex.RUnlock()

	defer func() {
		if e := recover(); e != nil {
			err = e.(error)
		}
	}()

	var found int32
	wg := &sync.WaitGroup{}
	errCh := make(chan error, len(d.handlers))
	for _, handler := range d.handlers {
		wg.Add(1)
		go d.dispatch(wg, errCh, &found, handler, cmd)
	}

	wg.Wait()
	close(errCh)

	if found != 1 {
		return &NoHandlerFoundError{
			Command: cmd,
		}
	}

	errs := []error{}
	for {
		e, ok := <-errCh
		if !ok {
			break
		}
		if e == nil {
			continue
		}
		errs = append(errs, e)
	}

	if len(errs) == 0 {
		return
	}

	err = errorgroup.New(errs)

	return
}
// Dispatch implements `Dispatcher.Dispatch`