# สัปดาห์ที่ 9: Asynchronous JavaScript Basics

## ภาพรวม

Asynchronous Programming เป็นพื้นฐานสำคัญในการพัฒนาเว็บสมัยใหม่ โดยเฉพาะการทำงานกับ API, Database หรือการประมวลผลที่ใช้เวลานาน ในสัปดาห์นี้เราจะเรียนรู้วิธีการเขียนโค้ด JavaScript ที่ไม่บล็อก (non-blocking code) เพื่อให้แอปพลิเคชันทำงานได้อย่างมีประสิทธิภาพ

---

## วัตถุประสงค์การเรียนรู้

เมื่อจบสัปดาห์นี้ นิสิตจะสามารถ:

1. ✅ อธิบายความแตกต่างระหว่าง **Synchronous** และ **Asynchronous Programming**
2. ✅ ใช้ `setTimeout()` และ `setInterval()` ได้อย่างถูกต้อง
3. ✅ สร้าง Promise และจัดการ `.then()`, `.catch()`, `.finally()` ได้
4. ✅ เขียนโค้ดโดยใช้ `async/await` เพื่อจัดการ asynchronous operations
5. ✅ ใช้ Error handling ใน asynchronous code ได้อย่างถูกต้อง
6. ✅ ใช้ `Promise.all()` สำหรับการดำเนินการหลายๆ อย่างพร้อมกัน

---

## สารบัญ (Table of Contents)

1. [Synchronous vs Asynchronous](#1-synchronous-vs-asynchronous)
2. [setTimeout และ setInterval](#2-settimeout-และ-setinterval)
3. [Promise](#3-promise)
4. [async/await](#4-asyncawait)
5. [Error Handling Patterns](#5-error-handling-patterns)
6. [ตัวอย่างการใช้งานจริง](#-ตัวอย่างการใช้งานจริง)
7. [Common Mistakes](#-common-mistakes-และวิธีแก้)
8. [แบบฝึกหัด](#-แบบฝึกหัด-exercises)
9. [Lab Assignment](#-lab-assignment)

---

## เนื้อหา

### 1. Synchronous vs Asynchronous

#### **Synchronous Code** (ทำงานทีละขั้นตอน)

```javascript
console.log("Start");
console.log("Processing...");
console.log("End");

// Output:
// Start
// Processing...
// End
```

**อธิบาย:** ในโค้ด synchronous โปรแกรมจะรอให้แต่ละบรรทัดเสร็จสิ้นก่อนจึงจะไปทำบรรทัดถัดไป การดำเนินการแต่ละคำสั่งจะบล็อก (block) ข้อมูลอื่นๆ ที่รอการดำเนินการ

```javascript
// ตัวอย่างอื่น - Synchronous
function calculateSum(a, b) {
  console.log("Calculating...");
  return a + b; // บรรทัดนี้ต้องทำให้เสร็จก่อนจึงจะคืนค่า
}

const result = calculateSum(5, 3);
console.log("Result:", result); // ต้องรอให้ calculateSum เสร็จก่อน
```

#### **Asynchronous Code** (ไม่รอให้เสร็จ ทำต่อไปได้เลย)

```javascript
console.log("First");

setTimeout(() => {
  console.log("Async Operation");
}, 2000);

console.log("Last");

// Output:
// First
// Last
// (รอ 2 วินาที)
// Async Operation
```

**อธิบาย:** ในโค้ด asynchronous โปรแกรมจะไม่รอให้ `setTimeout` เสร็จ แต่ไปดำเนินการบรรทัดถัดไปได้ทันที ฟังก์ชันที่เป็น asynchronous จะส่งการทำงาน "ไปทำในพื้นหลัง" และตัวโปรแกรมหลักจะทำงานต่อไปได้

**ประโยชน์ของ Asynchronous:**

- ไม่รอให้การดำเนินการชำรุ่ว / เสร็จ
- UI ไม่คล้ำง/freeze
- ประสิทธิภาพการทำงานดีขึ้น

```javascript
// ตัวอย่างจริง - เหมือนการเรียก API
function fetchData() {
  console.log("Fetching from API...");

  setTimeout(() => {
    console.log("Data received!"); // ทำงานอยู่ background
  }, 3000);

  return; // function นี้จบแล้ว แต่ setTimeout ยังทำงาน
}

console.log("Start");
fetchData();
console.log("End"); // Output ได้ทั้งตัว ไม่ต้องรอ fetchData
```

#### **Event Loop - ทำงานอย่างไร?** 🔄

**กำหนดการของการดำเนินการ Asynchronous:**

```
┌─────────────────────────────────────────────────────────┐
│                  Call Stack (ทำงาน)                 │
│   console.log('First') → setTimeout() → ...         │
└─────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────┐
│              Web APIs (JavaScript Runtime)          │
│   setTimeout, fetch, Promise, addEventListener       │
│   → "ไปทำงานในพื้นหลัง"                             │
└─────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────┐
│          Callback Queue (รอคิว)                     │
│   setTimeout callback, fetch response, etc.         │
└─────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────┐
│         Event Loop (ตรวจสอบคิว)                    │
│  "Call Stack ว่างหรือยัง? นำจาก Queue มาเรียก"   │
└─────────────────────────────────────────────────────────┘
```

**ตัวอย่างการไหล:**

```javascript
console.log("1: Start"); // ← Call Stack
setTimeout(() => {
  console.log("2: In setTimeout"); // ← Callback Queue → Event Loop → Call Stack
}, 0);
console.log("3: End"); // ← Call Stack

// Output:
// 1: Start
// 3: End
// 2: In setTimeout

// ที่เป็นเช่นนี้เพราะ:
// 1. console.log('1: Start') ทำงานเลย
// 2. setTimeout ส่งไป Web APIs (ไปรอลิจหลัง)
// 3. console.log('3: End') ทำงานเลย
// 4. Event Loop เห็น Call Stack ว่าง → นำ setTimeout callback เข้ามา
// 5. console.log('2: In setTimeout') ทำงาน
```

---

### 2. setTimeout และ setInterval

#### **setTimeout** - ทำงานครั้งเดียวหลังจากเวลาที่กำหนด

**Syntax:**

```javascript
setTimeout(callback, delayInMilliseconds, arg1, arg2, ...)
```

**Parameters:**

- `callback` - ฟังก์ชันที่จะดำเนินการ
- `delayInMilliseconds` - เวลาที่จะรอก่อนดำเนินการ (มิลลิวินาที)
- `arg1, arg2, ...` - arguments ที่ส่งไปยัง callback (optional)

**ตัวอย่าง:**

```javascript
// ตัวอย่าง 1: Arrow function
setTimeout(() => {
  console.log("This runs after 2 seconds");
}, 2000);

// ตัวอย่าง 2: Function reference
function greet() {
  console.log("Hello!");
}
setTimeout(greet, 1000);

// ตัวอย่าง 3: ส่ง arguments
function greetPerson(name, age) {
  console.log(`Hello ${name}, you are ${age} years old`);
}
setTimeout(greetPerson, 2000, "John", 25);

// ตัวอย่าง 4: ยกเลิก timeout
const timerId = setTimeout(() => {
  console.log("This will not run");
}, 5000);

clearTimeout(timerId); // ยกเลิก timeout ก่อนถึงเวลา

// ตัวอย่าง 5: nested setTimeout (ไม่ใช่วิธีที่ดี)
setTimeout(() => {
  console.log("First");
  setTimeout(() => {
    console.log("Second");
    setTimeout(() => {
      console.log("Third");
    }, 1000);
  }, 1000);
}, 1000);
```

#### **setInterval** - ทำงานซ้ำๆ ตามช่วงเวลาที่กำหนด

**Syntax:**

```javascript
setInterval(callback, intervalInMilliseconds, arg1, arg2, ...)
```

**ตัวอย่าง:**

```javascript
// ตัวอย่าง 1: นับเลขซ้ำๆ
let count = 0;

const intervalId = setInterval(() => {
  count++;
  console.log(`Count: ${count}`);

  if (count === 5) {
    clearInterval(intervalId); // หยุดเมื่อครบ 5 ครั้ง
  }
}, 1000);

// Output:
// Count: 1
// Count: 2
// Count: 3
// Count: 4
// Count: 5

// ตัวอย่าง 2: นาฬิกาดิจิทัล
function displayTime() {
  const now = new Date();
  console.log(now.toLocaleTimeString());
}

const clockId = setInterval(displayTime, 1000);

// หยุดนาฬิกาหลังจาก 5 วินาที
setTimeout(() => {
  clearInterval(clockId);
  console.log("Clock stopped");
}, 5000);

// ตัวอย่าง 3: Loading animation
let dots = 0;
const animationId = setInterval(() => {
  dots++;
  const loading = "Loading" + ".".repeat(dots % 4);
  console.clear();
  console.log(loading);

  if (dots > 20) {
    clearInterval(animationId);
  }
}, 500);
```

---

### 3. Promise

Promise เป็นวัตถุ (object) ที่แสดงถึงผลลัพธ์ของการทำงาน asynchronous ในอนาคต ซึ่งอาจจะสำเร็จ (fulfill) หรือล้มเหลว (reject)

**Promise คือสัญญา (promise) ว่าจะทำงานบางอย่างแล้วส่งผลลัพธ์กลับมา**

- สัญญาอาจจะสำเร็จ (fulfilled) หรือล้มเหลว (rejected)
- Promise เป็นวัตถุที่ชะลอการทำงาน แล้วทำให้เราสามารถรอผลลัพธ์ได้

**เปรียบเทียบ: "การสั่งอาหารที่ร้าน"**

- **Synchronous:** คุณรออยู่ที่เคาเตอร์เตอร์เพื่อสั่งแล้วรับอาหารเสร็จ (block ทั้งร้าน)
- **Promise:** คุณได้เลขคิว แล้วจะได้เรียกหมายเมื่ออาหารเสร็จ (non-blocking)

#### **สถานะของ Promise:**

- **Pending** - กำลังรอผลลัพธ์ (สถานะเริ่มต้น)
- **Fulfilled** - สำเร็จและมีค่า (resolved)
- **Rejected** - ล้มเหลวและมี error

#### **สร้าง Promise:**

```javascript
const myPromise = new Promise((resolve, reject) => {
  const success = true;

  setTimeout(() => {
    if (success) {
      resolve("Operation successful!"); // ส่งค่า
    } else {
      reject("Operation failed!"); // ส่ง error
    }
  }, 2000);
});
```

`Promise` รับ callback function ที่มี 2 parameter:

- `resolve` - เรียกเมื่อสำเร็จ
- `reject` - เรียกเมื่อล้มเหลว

#### **ใช้งาน Promise ด้วย `.then()`, `.catch()`, `.finally()`:**

```javascript
myPromise
  .then((result) => {
    console.log(result); // 'Operation successful!'
  })
  .catch((error) => {
    console.error(error); // 'Operation failed!'
  })
  .finally(() => {
    console.log("Promise completed - ทำความสะอาด");
  });
```

#### **Promise Chaining:**

```javascript
function fetchUser() {
  return new Promise((resolve) => {
    setTimeout(() => resolve({ id: 1, name: "John" }), 1000);
  });
}

function fetchPosts(userId) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(["Post 1", "Post 2"]), 1000);
  });
}

fetchUser()
  .then((user) => {
    console.log("User:", user);
    return fetchPosts(user.id); // ส่ง Promise ใหม่
  })
  .then((posts) => {
    console.log("Posts:", posts);
  })
  .catch((error) => {
    console.error("Error:", error);
  });
```

---

### 4. async/await

`async/await` เป็นไวยากรณ์ที่ทำให้เขียนโค้ด asynchronous อ่านง่ายขึ้น เหมือนกับการเขียนโค้ด synchronous ธรรมชาติ

#### **ฟังก์ชัน async:**

```javascript
// ฟังก์ชัน async จะคืน Promise
async function greeting() {
  return "Hello!";
}

// เทียบเท่ากับ:
function greeting() {
  return Promise.resolve("Hello!");
}

greeting().then((result) => console.log(result));
```

#### **ใช้ await ในฟังก์ชัน async:**

```javascript
async function getData() {
  const result = await myPromise; // รอจนกว่า Promise จะ resolve
  console.log(result);
}

getData();

// await สามารถใช้ได้เฉพาะใน async function เท่านั้น
```

#### **Error Handling ด้วย try/catch:**

```javascript
async function getData() {
  try {
    const user = await fetchUser();
    console.log("User:", user);

    const posts = await fetchPosts(user.id);
    console.log("Posts:", posts);
  } catch (error) {
    console.error("Error occurred:", error);
  } finally {
    console.log("Operations completed");
  }
}

getData();
```

#### **Sequential vs Parallel Execution:**

```javascript
// Sequential (ทีละอัน - ช้า ≈ 2000ms)
async function sequential() {
  const user1 = await fetchData(); // รอ 1000ms
  const user2 = await fetchData(); // รอ 1000ms
  console.log(user1, user2);
}

// Parallel (พร้อมกัน - เร็ว ≈ 1000ms)
async function parallel() {
  const [result1, result2] = await Promise.all([fetchData(), fetchData()]);
  console.log(result1, result2);
}

// Alternative (ไม่ใช้ destructuring)
async function parallelAlternative() {
  const results = await Promise.all([fetchData(), fetchData()]);
  console.log("Result 1:", results[0]);
  console.log("Result 2:", results[1]);
}

// Timing comparison
console.time("sequential");
sequential().then(() => console.timeEnd("sequential")); // ~2000ms

console.time("parallel");
parallel().then(() => console.timeEnd("parallel")); // ~1000ms
```

---

### 5. Error Handling Patterns

#### **Error Type ที่พบบ่อย:**

```javascript
// TypeError - เรียกใช้ property ที่ไม่มี
const obj = null;
obj.name; // ❌ TypeError: Cannot read property 'name' of null

// SyntaxError - เขียนโค้ดผิด syntax
const x = { name }; // ❌ SyntaxError: Unexpected identifier

// ReferenceError - ใช้ตัวแปรที่ไม่ declare
console.log(undefinedVar); // ❌ ReferenceError: undefinedVar is not defined

// NetworkError - ปัญหาการเชื่อมต่อ
fetch("invalid-url").catch((error) => console.error(error.message)); // "Failed to fetch"

// Custom Error
class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = "ValidationError";
  }
}
```

#### **ด้วย Promise:**

```javascript
fetchData()
  .then((data) => {
    console.log("Data:", data);
    return processData(data);
  })
  .then((result) => {
    displayResult(result);
  })
  .catch((error) => {
    // จัดการ error types ต่างๆ
    if (error instanceof TypeError) {
      console.error("Type Error:", error.message);
    } else if (error instanceof ReferenceError) {
      console.error("Reference Error:", error.message);
    } else if (error.message.includes("Failed to fetch")) {
      console.error("Network issue:", error.message);
    } else {
      console.error("Unknown error:", error);
    }
  });
```

#### **ด้วย async/await:**

```javascript
async function handleData() {
  try {
    const data = await fetchData();
    const result = await processData(data);
    displayResult(result);
  } catch (error) {
    if (error.name === "NetworkError") {
      console.error("Network issue:", error.message);
    } else {
      console.error("Unknown error:", error);
    }
  }
}

handleData();
```

---

## ตัวอย่างการใช้งานจริง

### ตัวอย่าง 1: Simulated API Call

```javascript
function simulateAPICall(endpoint) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (endpoint === "/users") {
        resolve([
          { id: 1, name: "Alice" },
          { id: 2, name: "Bob" },
        ]);
      } else {
        reject(new Error("Endpoint not found"));
      }
    }, 1500);
  });
}

async function loadUsers() {
  try {
    console.log("Loading users...");
    const users = await simulateAPICall("/users");
    console.log("Users loaded:", users);

    users.forEach((user) => {
      console.log(`- ${user.name}`);
    });
  } catch (error) {
    console.error("Failed to load users:", error.message);
  }
}

loadUsers();
```

### ตัวอย่าง 2: Countdown Timer

```javascript
function countdown(seconds) {
  return new Promise((resolve) => {
    let remaining = seconds;

    const timer = setInterval(() => {
      console.log(`${remaining}...`);
      remaining--;

      if (remaining < 0) {
        clearInterval(timer);
        console.log("Time is up!");
        resolve();
      }
    }, 1000);
  });
}

async function startCountdown() {
  console.log("Starting countdown from 5 seconds");
  await countdown(5);
  console.log("Countdown completed!");
}

startCountdown();
```

### ตัวอย่าง 3: Multiple Async Operations

```javascript
async function fetchMultipleResources() {
  try {
    // ทำงานทั้งหมดพร้อมกัน
    const [users, posts, comments] = await Promise.all([
      simulateAPICall("/users"),
      simulateAPICall("/posts"),
      simulateAPICall("/comments"),
    ]);

    console.log("Users:", users);
    console.log("Posts:", posts);
    console.log("Comments:", comments);
  } catch (error) {
    console.error("Error:", error.message);
  }
}

fetchMultipleResources();
```

### ตัวอย่าง 4: Promise.race() - เลือกผลลัพธ์ที่เร็วที่สุด

```javascript
// Promise.race() - เลือก Promise ที่ resolve/reject ก่อนสุด
async function fastestAPI() {
  try {
    // เลือก API ที่ตอบกลับเร็วสุด
    const result = await Promise.race([
      fetch("https://api1.example.com/data").then((r) => r.json()),
      fetch("https://api2.example.com/data").then((r) => r.json()),
      fetch("https://api3.example.com/data").then((r) => r.json()),
    ]);

    console.log("✅ Fastest response:", result);
    return result;
  } catch (error) {
    console.error("❌ All APIs failed or timed out:", error);
  }
}

fastestAPI();
```

### ตัวอย่าง 5: Retry Logic - พยายามใหม่หากล้มเหลว

```javascript
async function fetchWithRetry(url, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      console.log(`🔄 Attempt ${attempt}/${maxRetries}...`);
      const response = await fetch(url);

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }

      const data = await response.json();
      console.log("✅ Success!");
      return data;
    } catch (error) {
      console.error(`❌ Attempt ${attempt} failed:`, error.message);

      if (attempt === maxRetries) {
        console.error("❌ All retries exhausted!");
        throw error;
      }

      // รอก่อนพยายามใหม่ (exponential backoff)
      const delay = 1000 * attempt; // 1s, 2s, 3s
      console.log(`⏳ Waiting ${delay}ms before retry...`);
      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }
}

// ใช้งาน
fetchWithRetry("https://api.example.com/data")
  .then((data) => console.log("Final data:", data))
  .catch((error) => console.error("Failed to fetch:", error));
```

### ตัวอย่าง 6: Timeout Handler - กำหนดเวลาขีดจำกัด

```javascript
// สร้าง Promise ที่ timeout
function withTimeout(promise, timeoutMs) {
  return Promise.race([
    promise,
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error("Request timed out!")), timeoutMs),
    ),
  ]);
}

// ใช้งาน
async function fetchWithTimeout() {
  try {
    const data = await withTimeout(
      fetch("https://api.example.com/slow-endpoint").then((r) => r.json()),
      5000, // ขีดจำกัด 5 วินาที
    );
    console.log("✅ Data received:", data);
  } catch (error) {
    if (error.message === "Request timed out!") {
      console.error("⏱️ Request took too long!");
    } else {
      console.error("Error:", error);
    }
  }
}

fetchWithTimeout();
```

### ตัวอย่าง 7: AbortController - ยกเลิก Async Operations

**สถานการณ์จริง:** User navigate ออกจาก page แต่ fetch ยังทำงาน ทำให้ memory leak และ warning

```javascript
// สร้าง AbortController
const controller = new AbortController();

// ใช้ signal ส่งไปยัง fetch
async function fetchDataWithCancel(url) {
  try {
    const response = await fetch(url, {
      signal: controller.signal, // ส่ง signal
    });
    const data = await response.json();
    console.log("✅ Data:", data);
    return data;
  } catch (error) {
    if (error.name === "AbortError") {
      console.log("🛑 Request was cancelled");
    } else {
      console.error("Error:", error);
    }
  }
}

// เริ่มต้น
fetchDataWithCancel("https://api.example.com/data");

// ยกเลิก (เช่น user navigate ออก)
setTimeout(() => {
  console.log("⏱️ User navigated, cancelling request...");
  controller.abort(); // ⏹️ ยกเลิก fetch
}, 2000);
```

**ใช้ใน React:**

```javascript
useEffect(() => {
  const controller = new AbortController();

  fetchDataWithCancel(url);

  return () => {
    controller.abort(); // cleanup: ยกเลิกเมื่อ component unmount
  };
}, []);
```

### ตัวอย่าง 8: Fetch API Preview - สำหรับสัปดาห์หน้า

**Note:** Fetch API จะเรียนลึกในสัปดาห์หน้า แต่เรามาดูภาพรวมกันก่อน

```javascript
// ❌ Old way: Callback (ไม่ใช้แล้ว)
function getDataOld(url, callback) {
  // callback hell...
}

// ✅ Promise way: ยังใช้
const data = fetch(url)
  .then((response) => response.json())
  .catch((error) => console.error(error));

// ✅ Modern way: async/await
async function getData(url) {
  try {
    const response = await fetch(url);
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return await response.json();
  } catch (error) {
    console.error("Fetch error:", error);
  }
}

// ✅ With timeout (real-world):
async function getDataWithTimeout(url, timeout = 5000) {
  const controller = new AbortController();
  const id = setTimeout(() => controller.abort(), timeout);

  try {
    const response = await fetch(url, { signal: controller.signal });
    clearTimeout(id);
    return await response.json();
  } catch (error) {
    if (error.name === "AbortError") {
      console.error("⏱️ Request timed out");
    } else {
      console.error("Error:", error);
    }
  }
}
```

---

## ⚠️ Common Mistakes และวิธีแก้

### ❌ ผิด: ลืมใช้ `await`

```javascript
async function getData() {
  const data = fetchData(); // ได้ Promise ไม่ใช่ค่าจริง!
  console.log(data); // Promise { <pending> }
}
```

### ✅ ถูก:

```javascript
async function getData() {
  const data = await fetchData();
  console.log(data); // ค่าจริง
}
```

---

### ❌ ผิด: ลืม try/catch

```javascript
async function getData() {
  const data = await fetchData(); // Error จะทำให้ function crash!
}
```

### ✅ ถูก:

```javascript
async function getData() {
  try {
    const data = await fetchData();
  } catch (error) {
    console.error("Error:", error);
  }
}
```

---

### ❌ ผิด: ใช้ await ในลูป (Sequential)

```javascript
const items = ["item1", "item2", "item3"];

async function process() {
  for (const item of items) {
    await processItem(item); // ช้าว่า!
  }
}
```

### ✅ ถูก: (Parallel)

```javascript
const items = ["item1", "item2", "item3"];

async function process() {
  await Promise.all(items.map((item) => processItem(item)));
}
```

---

### ❌ ผิด: ลืมจัดการ Unhandled Promise Rejection

```javascript
// Danger: error ไม่มีใครจัดการ
fetch("/api/data")
  .then((r) => r.json())
  .then((data) => console.log(data));
// ถ้า fetch fail, error ไม่มี catch!
```

### ✅ ถูก: จัดการทุก edge case

```javascript
// วิธีที่ 1: .catch()
fetch("/api/data")
  .then((r) => r.json())
  .then((data) => console.log(data))
  .catch((error) => console.error("Fetch error:", error));

// วิธีที่ 2: try/catch
async function safeFetch() {
  try {
    const response = await fetch("/api/data");
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error("Error:", error);
  }
}

// วิธีที่ 3: Global fallback (เป็นประโยชน์มาก)
window.addEventListener("unhandledrejection", (event) => {
  console.error("🚨 Unhandled rejection:", event.reason);
  // ส่งไปยัง error tracking service (Sentry, etc.)
});
```

---

### ❌ ผิด: Race Condition ในการเรียก async สองครั้ง

```javascript
let data = null;

async function fetchData() {
  const result = await fetch("/api/data");
  data = result; // ❌ ถ้าเรียก fetchData() 2 ครั้งพร้อมกัน?
  // race condition: result ไหนจะ assign สุดท้าย?
}

fetchData(); // ครั้งที่ 1
fetchData(); // ครั้งที่ 2 - conflict!
```

### ✅ ถูก: return ผลลัพธ์แทนการ assign global

```javascript
async function fetchData() {
  const response = await fetch("/api/data");
  return response.json(); // ✅ ปลอดภัย race condition
}

const result1 = await fetchData();
const result2 = await fetchData();
```

---

### ❌ ผิด: Memory Leak - ไม่ cancel async operation

```javascript
// Component mount
fetch("/api/large-data")
  .then((r) => r.json())
  .then((data) => {
    setState(data); // ❌ ถ้า component unmount ก่อนจบ?
    // setState ยังทำงานแล้ว memory leak + warning!
  });

// Component unmount - fetch ยังคิด
```

### ✅ ถูก: Cancel async ในเมื่อ unmount

```javascript
useEffect(() => {
  const controller = new AbortController();

  fetch("/api/large-data", { signal: controller.signal })
    .then((r) => r.json())
    .then((data) => setState(data))
    .catch((err) => {
      if (err.name !== "AbortError") {
        console.error(err);
      }
    });

  return () => {
    controller.abort(); // ✅ cleanup: ยกเลิก fetch
  };
}, []);
```

---

## 📝 แบบฝึกหัด (Exercises)

### Exercise 1: สร้าง Promise ที่ random

สร้าง function ที่ return Promise ซึ่งจะ resolve หรือ reject แบบ random:

```javascript
function randomPromise() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const random = Math.random();
      if (random > 0.5) {
        resolve(`Success! Random: ${random.toFixed(2)}`);
      } else {
        reject(`Failed! Random: ${random.toFixed(2)}`);
      }
    }, 1000);
  });
}

// ใช้ .then/.catch
randomPromise()
  .then((result) => console.log(result))
  .catch((error) => console.error(error));
```

### Exercise 2: ใช้ async/await กับ Multiple Promises

```javascript
function delay(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function processSteps() {
  console.log("Step 1: Start");
  await delay(1000);

  console.log("Step 2: Processing...");
  await delay(1000);

  console.log("Step 3: Almost done...");
  await delay(1000);

  console.log("Step 4: Complete!");
}

processSteps();
```

### Exercise 3: Parallel Execution

```javascript
function fetchUserData(userId) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ id: userId, name: `User ${userId}` });
    }, 1000);
  });
}

async function loadMultipleUsers() {
  try {
    // ทำงานพร้อมกัน (ทั้งหมด ≈ 1000ms)
    const [user1, user2, user3] = await Promise.all([
      fetchUserData(1),
      fetchUserData(2),
      fetchUserData(3),
    ]);

    console.log("All users:", user1, user2, user3);
  } catch (error) {
    console.error("Error:", error);
  }
}

loadMultipleUsers();
```

---

## 🎯 Lab Assignment (Extended)

### Traffic Light Simulator - Version 1 (Basic) 🚦

สร้างโปรแกรม Traffic Light ที่:

- แสดงสีไฟ: 🔴 Red (3s) → 🟡 Yellow (2s) → 🟢 Green (3s)
- วนซ้ำต่อไปเรื่อยๆ
- มีปุ่ม Start/Stop

**Starter Code:**

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Traffic Light Simulator</title>
    <style>
      body {
        display: flex;
        justify-content: center;
        align-items: center;
        min-height: 100vh;
        background-color: #f0f0f0;
      }

      .container {
        text-align: center;
      }

      .traffic-light {
        display: flex;
        flex-direction: column;
        gap: 10px;
        background: #333;
        padding: 20px;
        border-radius: 20px;
        margin: 0 auto 30px;
      }

      .light {
        width: 100px;
        height: 100px;
        border-radius: 50%;
        opacity: 0.3;
        transition: opacity 0.2s;
      }

      .light.red {
        background: red;
      }
      .light.yellow {
        background: yellow;
      }
      .light.green {
        background: green;
      }
      .light.active {
        opacity: 1;
      }

      button {
        padding: 10px 20px;
        margin: 5px;
        font-size: 16px;
        cursor: pointer;
        border: none;
        border-radius: 5px;
        background-color: #007bff;
        color: white;
      }

      button:hover {
        background-color: #0056b3;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <h1>Traffic Light Simulator</h1>

      <div class="traffic-light">
        <div class="light red" id="red"></div>
        <div class="light yellow" id="yellow"></div>
        <div class="light green" id="green"></div>
      </div>

      <div>
        <button id="startBtn">Start</button>
        <button id="stopBtn">Stop</button>
      </div>
    </div>

    <script>
      // Your async/await code here
    </script>
  </body>
</html>
```

**Requirements:**

- ✅ ใช้ async/await
- ✅ ใช้ Promise และ setTimeout
- ✅ ปุ่ม Start เริ่มวัฏจักร
- ✅ ปุ่ม Stop หยุดวัฏจักร
- ✅ สีไฟต้องสว่างตอน active เท่านั้น

**Solution Hints:**

```javascript
// Step 1: สร้าง function ที่ return Promise
function showLight(color, durationMs) {
  return new Promise((resolve) => {
    // หาเลข element ด้วย getElementById
    // เปลี่ยนสี ด้วย classList.add('active')
    // หลัง durationMs มิลลิวินาที ให้ remove class แล้ว resolve

    setTimeout(() => {
      // .... code .....
      resolve();
    }, durationMs);
  });
}

// Step 2: สร้าง async function สำหรับวัฏจักร
async function trafficLightCycle() {
  while (isRunning) {
    // isRunning เป็น flag สำหรับ Start/Stop
    await showLight("red", 3000);
    await showLight("yellow", 2000);
    await showLight("green", 3000);
  }
}

// Step 3: เพิ่ม event listener
document.getElementById("startBtn").addEventListener("click", () => {
  isRunning = true;
  trafficLightCycle();
});

document.getElementById("stopBtn").addEventListener("click", () => {
  isRunning = false;
});
```

---

### Traffic Light Simulator - Version 2 (Challenge) 🏆

**ต้องการ:**

- ✅ ทำงาน pause/resume (ไม่ใช่แค่ stop)
- ✅ แสดง countdown timer ("2 seconds left...")
- ✅ Handle error: ถ้า DOM element หาไม่เจอ ให้ error gracefully
- ✅ ยกเลิก cycle ถ้า component "destroy" (ใช้ AbortController)

**Hints for Version 2:**

```javascript
// สำหรับ pause/resume ใช้ AbortController
const controller = new AbortController();
const signal = controller.signal;

async function showLightWithCountdown(color, durationMs) {
  const element = document.getElementById(color);
  if (!element) throw new Error(`Element #${color} not found`);

  element.classList.add("active");

  return new Promise((resolve, reject) => {
    const timeout = setTimeout(() => {
      element.classList.remove("active");
      resolve();
    }, durationMs);

    signal.addEventListener("abort", () => {
      clearTimeout(timeout);
      element.classList.remove("active");
      reject(new Error("Cancelled"));
    });
  });
}

// สำหรับ pause ใช้ Promise.race กับ pause signal
async function pausableDelay(ms) {
  const pausePromise = new Promise((resolve) => {
    window.addEventListener("pause", () => resolve("paused"));
  });

  return Promise.race([
    new Promise((resolve) => setTimeout(resolve, ms)),
    pausePromise,
  ]);
}

// Error handling
try {
  await trafficLightCycle();
} catch (error) {
  if (error.name === "AbortError") {
    console.log("Cycle cancelled");
  } else {
    console.error("Error:", error.message);
  }
}
```

---

## 📚 Additional Resources

- [MDN: Asynchronous JavaScript](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous)
- [MDN: Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
- [MDN: async function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)
- [MDN: await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await)
- [JavaScript.info: Promises](https://javascript.info/promise-basics)
- [JavaScript.info: Async/Await](https://javascript.info/async-await)

---

## 📌 Key Takeaways

✅ Asynchronous code ไม่บล็อก (non-blocking) การทำงาน  
✅ Promise จัดการ asynchronous operations ได้ดี  
✅ async/await ทำให้โค้ด asynchronous อ่านง่ายขึ้น  
✅ **ต้องใช้ try/catch เสมอ** เพื่อจัดการ errors  
✅ ใช้ `Promise.all()` สำหรับการทำงานหลายๆ อย่างพร้อมกัน  
✅ ระวังการใช้ await ในลูป (อาจช้า)

---

**Next Week:** Fetch API & Single Page Application (SPA) Start
