Передача данных от обработчика к обработчику требует сохранения промежуточного состояния -- контекста. Контекст можно расширять или сужать, но по частям он смысла не имеет.
Контекст -- весь необходимый набор данных для выполнени операций.
ctxBg := context.Background()
Глобальный контекст использовать нельзя!
Дочерний контекст с простой отменой:
func FindRecord(ctxApp app.AppContext, numRecord string) error {
ctxWork, fnCancel := context.WithCancel(ctxApp)
defer fnCancel()
chWork := make(chan int, 2)
fnRead :=func(){
defer close(chWork)
err:=file.FindRecord(numRecord)
if err!=nil{
ctxApp.Cancel()
}
}
go fnRead()
select {
case <-ctxApp.Done(): // Отмена контекста приложения
return fmt.Errorf("FindRecord(): cancel ctxApp")
// Ожидание окончания работы, всё-равно
// когда-нибудь закончится так или иначе.
case <-chWork:
return nil
}
}
// LocalStorage -- локальное хранилище ключ-значение
type LocalStorage interface {
// Получить значение п оключу
Get(string) (*IndexResponse, error)
}
// IndexRequest -- запрос индексной страницы
type IndexRequest struct {
Name string // Имя на индексной странице
}
// IndexResponse -- ответ на запрос инжексной страницы
type IndexResponse struct {
// Определение полей структуры IndexResponse
}
var (
db LocalStorage // Объект локального хранилища
)
// Index -- запрос индексной страницы
func Index(ctx context.Context, request *IndexRequest) (*IndexResponse, error) {
// Дочерний контекст и ег офункция отмены
ctxTimeout, fnCancel := context.WithTimeout(ctx, time.Millisecond*500)
// Обязательный вызов отмены контекста при выходе, иначе будет утечка памяти!!
defer fnCancel()
type Resp struct {
resp *IndexResponse // Полезный ответ (если нет ошибки)
err error // Ошибка (если была)
}
chResp := make(chan *Resp, 1) // Канал для получения результата после обработки
// Эта функция должна работат ьв отдельном потоке. Иначе невозможно
// устроить соревнование между глобальным контекстом и дочерним.
fnWork := func() {
defer close(chResp) // Обязательн озакрыт ьканал при выходе
resp, err = db.Get(request.Name)
if err != nil { // Была ошибка -- вернём только ошибку
resp := &Resp {
err: err,
}
chResp <- resp
return
}
resp := &Resp { // Ошибки не было -- значит вернём только результат.
err: err,
}
chResp <- resp
}
go fnWork() // Обязательно! Запуск в отдельном потоке
select { // Здесь гонки между таймаутом и полезным результатом -- кто быстрее.
case <-ctxTimeout.Done(): // Был ли таймаут дочернего контекста?
return nil, fmt.Errorf("Index(): timeout in make response")
case result := <-chResp: // Что там с результатом?
if result.err != nil {
return nil, fmt.Errorf("Index(): in make response, err=\n\t%w", result.err)
}
// Всё ок, возвращаем полезный результат
return result.resp, nil
}
}