Error Handling
Channel Error Handling Strategies
Basic Error Propagation
func processData(ch <-chan int) error {
for v := range ch {
if err := validateData(v); err != nil {
return fmt.Errorf("data validation error: %w", err)
}
}
return nil
}
Error Channel Pattern
graph LR
A[Goroutine] -->|Result| B[Result Channel]
A -->|Error| C[Error Channel]
Implementing Error Channels
func workerWithErrorHandling(jobs <-chan int, results chan<- int, errors chan<- error) {
for job := range jobs {
result, err := processJob(job)
if err != nil {
errors <- err
return
}
results <- result
}
}
func main() {
jobs := make(chan int, 10)
results := make(chan int, 10)
errors := make(chan error, 10)
go func() {
for {
select {
case err := <-errors:
handleError(err)
case result := <-results:
processResult(result)
}
}
}()
}
Error Handling Techniques
Technique |
Description |
Use Case |
Error Channels |
Separate error communication |
Concurrent error handling |
Context Cancellation |
Propagate cancellation signals |
Timeout and cancellation |
Panic and Recover |
Handle unrecoverable errors |
Last-resort error management |
Context-Based Error Handling
func operationWithContext(ctx context.Context) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
// Perform operation
return nil
}
}
Advanced Error Handling Patterns
Graceful Degradation
func redundantOperation(primary, backup <-chan int) int {
select {
case result := <-primary:
return result
case result := <-backup:
return result
case <-time.After(5 * time.Second):
return defaultValue
}
}
Error Aggregation
func aggregateErrors(errorChannels ...<-chan error) <-chan error {
var wg sync.WaitGroup
aggregated := make(chan error)
multiplex := func(ch <-chan error) {
defer wg.Done()
for err := range ch {
aggregated <- err
}
}
wg.Add(len(errorChannels))
for _, ch := range errorChannels {
go multiplex(ch)
}
go func() {
wg.Wait()
close(aggregated)
}()
return aggregated
}
Best Practices
- Use dedicated error channels
- Implement timeout mechanisms
- Provide meaningful error messages
- Use context for cancellation
- Log errors appropriately
Error Handling Anti-Patterns
- Ignoring errors
- Excessive error suppression
- Complex error handling logic
- Minimize error channel allocations
- Use buffered error channels
- Implement efficient error routing
Example: Comprehensive Error Handling
func complexOperation(ctx context.Context, input <-chan Data) (<-chan Result, <-chan error) {
results := make(chan Result)
errors := make(chan error, 1)
go func() {
defer close(results)
defer close(errors)
for data := range input {
select {
case <-ctx.Done():
errors <- ctx.Err()
return
default:
result, err := processData(data)
if err != nil {
errors <- err
return
}
results <- result
}
}
}()
return results, errors
}
LabEx recommends developing robust error handling strategies to create resilient concurrent applications.