JavaScript Loop (for/while) ทำงานแบบ Synchronous ไม่มีการหน่วงเวลา (Delay) ในตัว ต้องใช้ setTimeout หรือ async/await ร่วมกับ Promise เพื่อสร้าง Delay ระหว่างรอบ
วิธีที่ 1: setTimeout ใน Loop (Classic)
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Delay in Loop</title>
<style>
body { font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; padding: 20px; }
.result { font-size: 18px; font-weight: 500; color: blueviolet; font-family: monospace; }
button { padding: 8px 16px; margin: 10px 0; cursor: pointer; }
</style>
</head>
<body>
<h1>เพิ่มหน่วงเวลาใน Loop</h1>
<div class="result" id="result"></div>
<button class="Btn" id="startBtn">เริ่ม Loop (Delay 2 วินาที)</button>
<h3>กดปุ่มเพื่อเริ่ม Loop แสดง i = 0 ถึง 9 หน่วง 2 วินาทีต่อรอบ</h3>
<script>
const btn = document.getElementById("startBtn");
const result = document.getElementById("result");
btn.addEventListener("click", () => {
result.innerHTML = "";
btn.disabled = true;
for (let i = 0; i < 10; i++) {
// ใช้ IIFE หรือ let (Block Scope) เพื่อ Capture ค่า i ให้ถูกต้อง
setTimeout(() => {
result.innerHTML += `i = ${i}
`;
if (i === 9) btn.disabled = false;
}, 2000 * i); // Delay เพิ่มขึ้นทีละ 2 วินาที
}
});
</script>
</body>
</html>
ผลลัพธ์
กดปุ่ม → รอ 2 วินาที → แสดง "i = 0"
รออีก 2 วินาที → แสดง "i = 1"
... จนถึง "i = 9" (รวม ~18 วินาที)
⚠️ ข้อผิดพลาดที่พบบ่อย: ใช้ var แทน let
// ❌ ผิด - ใช้ var (Function Scope)
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1000 * i);
}
// ผลลัพธ์: 3, 3, 3 (เพราะ var มีค่า 3 ตอน setTimeout ทำงาน)
// ✅ ถูก - ใช้ let (Block Scope)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1000 * i);
}
// ผลลัพธ์: 0, 1, 2 (let สร้าง Binding ใหม่ทุก Loop)
วิธีที่ 2: async/await + Promise (Modern, อ่านง่ายกว่า)
<script>
// Helper: สร้าง Promise ที่ Resolve หลัง ms วินาที
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
async function runLoopWithDelay() {
const result = document.getElementById("result");
result.innerHTML = "";
for (let i = 0; i < 10; i++) {
await delay(2000); // รอ 2 วินาที
result.innerHTML += `i = ${i}
`;
}
btn.disabled = false;
}
btn.addEventListener("click", runLoopWithDelay);
</script>
วิธีที่ 3: Sequential Async Operations (รอทีละอย่าง)
async function processItems(items) {
const results = [];
for (const item of items) {
const result = await processItem(item); // รอแต่ละตัว
results.push(result);
}
return results;
}
// vs Parallel (พร้อมกัน) - เร็วกว่า
async function processItemsParallel(items) {
return Promise.all(items.map(item => processItem(item)));
}
กรณีการใช้งานจริง
1. Animation / Typing Effect
async function typeText(element, text, speed = 100) {
element.textContent = "";
for (const char of text) {
element.textContent += char;
await delay(speed);
}
}
2. API Rate Limiting (ส่ง Request ทีละอัน)
async function fetchAllWithRateLimit(urls, delayMs = 1000) {
const results = [];
for (const url of urls) {
const response = await fetch(url);
results.push(await response.json());
await delay(delayMs); // หน่วงระหว่าง Request
}
return results;
}
3. Polling / Long Polling
async function pollUntilReady(checkFn, interval = 5000, maxAttempts = 10) {
for (let i = 0; i < maxAttempts; i++) {
if (await checkFn()) return true;
await delay(interval);
}
throw new Error("Timeout: Condition not met");
}
สรุป: เลือกวิธีไหนดี?
| วิธี | เหมาะสำหรับ | ข้อดี | ข้อเสีย |
|---|---|---|---|
| setTimeout ใน Loop | Simple, Fire-and-forget | ไม่ Block Main Thread | Debug ยาก, Callback Hell |
| async/await + delay() | Sequential Logic, Readable | อ่านง่าย, Try/Catch ได้, Debug ง่าย | Block Function (แต่ไม่ Block Thread) |
| setInterval | Repeating ทุกๆ ช่วงเวลา | ง่ายสำหรับ Interval | ไม่แม่นยำ, ยากควบคุม Stop |
| requestAnimationFrame | Animation (60fps) | Sync กับ Browser Repaint | ไม่ใช่ Delay ทั่วไป |