Frontend Labs
⋯

Day 8 — Control Flow & defer

Learning Objectives

  • Understand defer execution order and why it's essential for resource cleanup
  • Use switch without fallthrough for clean dispatch
  • Know when for replaces while, do-while, and loop

Key Concepts

// defer — runs when the surrounding function returns, LIFO order
// Arguments are evaluated when defer is called, not when it runs
func processRequest(conn net.Conn) {
    defer conn.Close()          // runs last: cleanup
    defer logRequest(time.Now()) // evaluated NOW, logged later

    // ... process ...
}

// Multiple defers — LIFO (last in, first out)
func example() {
    defer fmt.Println("3") // runs third
    defer fmt.Println("2") // runs second
    defer fmt.Println("1") // runs first
}
// Output: 1, 2, 3

// switch — no fallthrough by default (unlike C/Java)
switch r.Method {
case "GET", "HEAD":
    handleRead(w, r)
case "POST", "PUT", "PATCH":
    handleWrite(w, r)
case "DELETE":
    handleDelete(w, r)
default:
    http.Error(w, "method not allowed", 405)
}

// for — the only loop in Go
for i := 0; i < 10; i++ { }      // C-style
for condition { }                   // while-style
for { }                             // infinite loop
for i, v := range slice { }        // range
for k, v := range m { }            // map range

Exercise: Request Lifecycle Simulator

package main

import (
    "fmt"
    "sync/atomic"
    "time"
)

var activeRequests atomic.Int64

func simulateRequest(id int) {
    activeRequests.Add(1)

    // Defers register in order, execute in reverse — critical for connection cleanup
    defer func() {
        activeRequests.Add(-1)
        fmt.Printf("[%d] counter decremented, active=%d\n", id, activeRequests.Load())
    }()
    defer func() { fmt.Printf("[%d] 3. log request complete\n", id) }()
    defer func() { fmt.Printf("[%d] 2. release connection to pool\n", id) }()
    defer func() { fmt.Printf("[%d] 1. close response body\n", id) }()

    fmt.Printf("[%d] processing...\n", id)
    time.Sleep(10 * time.Millisecond)
    fmt.Printf("[%d] done processing\n", id)
}

func main() {
    for i := 1; i <= 3; i++ {
        simulateRequest(i)
        fmt.Println("---")
    }
    fmt.Printf("final active requests: %d\n", activeRequests.Load())
}

Expected output shows cleanup in reverse order: close body → release conn → log → decrement

Real-World Context

Leaked connections cause CLOSE_WAIT sockets (you'll debug these in Day 52). Every net.Conn, http.Response.Body, and os.File must be defer-closed. The order matters: release the connection after you close the body.


Generated from week2.md · 8 Oct 2026 16:36