Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> JavaScript

เพิ่มหน่วงเวลา (Delay) ใน Loop ด้วย JavaScript

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 ใน LoopSimple, Fire-and-forgetไม่ Block Main ThreadDebug ยาก, Callback Hell
async/await + delay()Sequential Logic, Readableอ่านง่าย, Try/Catch ได้, Debug ง่ายBlock Function (แต่ไม่ Block Thread)
setIntervalRepeating ทุกๆ ช่วงเวลาง่ายสำหรับ Intervalไม่แม่นยำ, ยากควบคุม Stop
requestAnimationFrameAnimation (60fps)Sync กับ Browser Repaintไม่ใช่ Delay ทั่วไป