Ошибка context.DeadlineExceeded

Ошибка context.DeadlineExceeded возникает, когда контекст превышает установленный срок выполнения операции. Эта ошибка возвращается функцией Context.Err после того, как истекло время, установленное с помощью context.WithTimeout или context.WithDeadline. Ошибка информирует о том, что операция не успела завершиться в отведенное время.

Синтаксис

Ошибка context.DeadlineExceeded является предопределенной переменной пакета context. Она реализует интерфейс error и может быть использована для проверки причины завершения контекста:

goif errors.Is(ctx.Err(), context.DeadlineExceeded) {
  // обработка превышения времени
}

Также можно сравнивать ошибку с предопределенной переменной:

goif ctx.Err() == context.DeadlineExceeded {
  // обработка превышения времени
}

Пример использования с таймаутом

Создадим контекст с таймаутом в 1 секунду и выполним операцию, которая занимает 2 секунды:

gopackage main

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

func main() {
  ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
  defer cancel()

  select {
  case <-time.After(2 * time.Second):
    fmt.Println("Operation completed")
  case <-ctx.Done():
    fmt.Println(ctx.Err())
  }
}

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

go"context deadline exceeded"

В этом примере операция не успевает завершиться за 1 секунду, и контекст возвращает ошибку context.DeadlineExceeded.

Пример с жестким дедлайном

Установим конкретное время завершения операции с помощью context.WithDeadline:

gopackage main

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

func main() {
  deadline := time.Now().Add(500 * time.Millisecond)
  ctx, cancel := context.WithDeadline(context.Background(), deadline)
  defer cancel()

  select {
  case <-time.After(1 * time.Second):
    fmt.Println("Operation completed")
  case <-ctx.Done():
    fmt.Println(ctx.Err())
  }
}

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

go"context deadline exceeded"

Обработка ошибки в горутинах

При работе с горутинами важно проверять ошибку контекста и корректно завершать выполнение:

gopackage main

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

func doWork(ctx context.Context) error {
  select {
  case <-time.After(3 * time.Second):
    return nil
  case <-ctx.Done():
    return ctx.Err()
  }
}

func main() {
  ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
  defer cancel()

  err := doWork(ctx)
  if errors.Is(err, context.DeadlineExceeded) {
    fmt.Println("Timeout exceeded")
  } else if err != nil {
    fmt.Println("Other error:", err)
  } else {
    fmt.Println("Work completed successfully")
  }
}

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

go"Timeout exceeded"

Сравнение с context.Canceled

Важно различать context.DeadlineExceeded и context.Canceled. Первая возникает при превышении времени, вторая - при явной отмене:

gopackage main

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

func main() {
  ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
  defer cancel()

  go func() {
    time.Sleep(500 * time.Millisecond)
    cancel()
  }()

  select {
  case <-time.After(3 * time.Second):
    fmt.Println("Operation completed")
  case <-ctx.Done():
    fmt.Println(ctx.Err())
  }
}

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

go"context canceled"

В этом примере контекст был отменен явно до истечения таймаута, поэтому возвращается ошибка context.Canceled.

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

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