Конкурентный загрузчик веб‑страниц

Замените Sleep на WaitGroup: Add вызывается до запуска, Done — через defer, Wait — после цикла. В каждом запросе обработайте сетевую ошибку, HTTP-статус и чтение тела.

Короткий ответ

Что ответить на собеседовании

Замените Sleep на WaitGroup: Add вызывается до запуска, Done — через defer, Wait — после цикла. В каждом запросе обработайте сетевую ошибку, HTTP-статус и чтение тела.

Подробный разбор

Ответ с пояснениями

Условие

Необходимо доработать программу, чтобы:

  • избавиться от использования time.Sleep для ожидания завершения горутин;
  • корректно обрабатывать возникающие ошибки;
  • дождаться окончания выполнения всех запросов.
package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    urls := []string{
        "https://www.lamoda.ru",
        "https://www.yandex.ru",
        "https://www.mail.ru",
        "https://www.google.com",
    }

    for _, url := range urls {
        go func(url string) {
            fmt.Printf("Fetching %s...\n", url)

            err := fetchUrl(url)
            if err != nil {
                fmt.Printf("Error fetching %s: %v\n", url, err)
                return
            }

            fmt.Printf("Fetched %s\n", url)
        }(url)
    }

    fmt.Println("All requests launched!")
    time.Sleep(400 * time.Millisecond)
    fmt.Println("Program finished.")
}

func fetchUrl(url string) error {
    // Подробная реализация опущена и не относится к теме задачи
    // _, err := http.Get(url)
    return nil
}

Ответ

Замените Sleep на WaitGroup: Add вызывается до запуска, Done — через defer, Wait — после цикла. В каждом запросе обработайте сетевую ошибку, HTTP-статус и чтение тела.

Ниже демонстрационный список URL можно заменить списком из условия. Импорты: fmt, io, net/http, sync, time.

func main() {
    urls := []string{"https://example.com", "https://example.org"}
    client := &http.Client{Timeout: 5 * time.Second}
    var wg sync.WaitGroup
    for _, url := range urls {
        wg.Add(1)
        go func(url string) {
            defer wg.Done()
            resp, err := client.Get(url)
            if err != nil {
                fmt.Printf("Error fetching %s: %v\n", url, err)
                return
            }
            defer resp.Body.Close()
            if resp.StatusCode < 200 || resp.StatusCode >= 300 {
                fmt.Printf("Error fetching %s: HTTP %d\n", url, resp.StatusCode)
                return
            }
            _, err = io.Copy(io.Discard, resp.Body)
            if err != nil {
                fmt.Printf("Error reading %s: %v\n", url, err)
                return
            }
            fmt.Printf("Fetched %s\n", url)
        }(url)
    }
    wg.Wait()
    fmt.Println("Program finished.")
}

Успех HTTP не гарантируется отсутствием ошибки Get, поэтому статус проверяется отдельно. Тайм-аут ограничивает всё обращение. Для большого списка добавьте ограниченную конкурентность; для недоверенных ответов — лимит объёма тела. Здесь данные прочитываются и отбрасываются, файлы на диск не сохраняются.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее