Функция context.WithCancelCause

Функция context.WithCancelCause создает новый производный контекст на основе родительского. В отличие от context.WithCancel, она позволяет передать причину отмены в виде ошибки. Первый параметр функции — родительский контекст, от которого будет создан производный. Функция возвращает три значения: производный контекст, функцию отмены с причиной и функцию, которая позволяет получить текущую причину отмены.

Синтаксис

goctx, cancelCause := context.WithCancelCause(parent)

Пример

Давайте создадим контекст с возможностью отмены по причине:

gopackage main

import (
  "context"
  "fmt"
)

func main() {
  ctx, cancelCause := context.WithCancelCause(context.Background())

  // Отменяем контекст с указанием причины
  cancelCause(fmt.Errorf("timeout exceeded"))

  // Получаем причину отмены
  err := context.Cause(ctx)
  fmt.Println(err)
}

Результат выполнения кода:

go"timeout exceeded"

Пример

Рассмотрим пример с горутиной, которая выполняет работу до отмены:

gopackage main

import (
  "context"
  "fmt"
  "time"
)

func main() {
  ctx, cancelCause := context.WithCancelCause(context.Background())

  go func() {
    for {
      select {
      case <-ctx.Done():
        fmt.Println("Work stopped:", context.Cause(ctx))
        return
      default:
        fmt.Println("Working...")
        time.Sleep(500 * time.Millisecond)
      }
    }
  }()

  time.Sleep(2 * time.Second)
  cancelCause(fmt.Errorf("manual stop"))
  time.Sleep(500 * time.Millisecond)
}

Результат выполнения кода:

go"Working..."
"Working..."
"Working..."
"Working..."
"Work stopped: manual stop"

Пример

Покажем, как отменить контекст с причиной, и как получить эту причину:

gopackage main

import (
  "context"
  "errors"
  "fmt"
)

func main() {
  ctx, cancelCause := context.WithCancelCause(context.Background())

  // Отменяем с конкретной ошибкой
  cancelCause(errors.New("connection lost"))

  // Проверяем причину
  if err := context.Cause(ctx); err != nil {
    fmt.Println("Error:", err)
  }
}

Результат выполнения кода:

go"Error: connection lost"

Пример

Использование с таймаутом для контроля времени выполнения:

gopackage main

import (
  "context"
  "fmt"
  "time"
)

func main() {
  ctx, cancelCause := context.WithCancelCause(context.Background())

  go func() {
    select {
    case <-time.After(3 * time.Second):
      cancelCause(fmt.Errorf("operation timed out"))
    }
  }()

  time.Sleep(2 * time.Second)
  err := context.Cause(ctx)
  if err != nil {
    fmt.Println("Context cancelled:", err)
  } else {
    fmt.Println("Context is still active")
  }
}

Результат выполнения кода:

go"Context is still active"

Смотрите также

  • функцию context.WithCancel,
    которая создает контекст с отменой без указания причины
  • функцию context.Cause,
    которая возвращает причину отмены контекста
  • функцию context.WithTimeout,
    которая создает контекст с таймаутом
  • функцию context.WithDeadline,
    которая создает контекст с дедлайном