สวัสดีครับ! วันนี้ผมจะมาสอนเทคนิคสำคัญที่ทุกคนควรรู้เมื่อทำงานกับ API นั่นก็คือ การยกเลิกคำขอ หรือ Cancel Request ครับ

เคยไหมครับที่กดปุ่มค้นหาแล้วรอนานมาก แล้วอยากกดยกเลิก? หรือเผลอกดซ้ำหลายครั้งจนเกิดปัญหา? บทความนี้จะแก้ปัญหานี้ให้หมดเลยครับ

ทำไมต้องยกเลิกคำขอ API?

ลองนึกภาพว่าคุณกำลังสร้างแอปที่ดึงข้อมูลจาก API ของ HolySheep AI แต่ข้อมูลใหญ่มากจนใช้เวลานาน หรือผู้ใช้กดย้ายหน้าไปอีกหน้าแล้ว คำขอเดิมก็ยังทำงานต่อ สิ้นเปลืองทรัพยากรเครื่องโดยใช่เหตุ

ปัญหาที่จะเกิดขึ้น:

AbortController คืออะไร?

AbortController คือเครื่องมือที่มาพร้อมกับเบราว์เซอร์ทุกตัวในปัจจุบัน (Chrome, Firefox, Safari, Edge) ช่วยให้เราสั่งยกเลิกคำขอ HTTP ได้เมื่อต้องการ

วิธีทำงานง่ายมาก:

เริ่มต้นใช้งาน AbortController ทีละขั้นตอน

ขั้นตอนที่ 1: สร้าง AbortController

ก่อนอื่นเราต้องสร้าง Object ที่ชื่อ AbortController ขึ้นมาก่อน ง่ายมากครับ:

// สร้าง AbortController ใหม่
const controller = new AbortController();

เมื่อสร้างแล้ว เราจะได้ controller.signal ซึ่งเป็น "สัญญาณ" ที่จะส่งไปกับคำขอ และ controller.abort() ซึ่งเป็นฟังก์ชันสำหรับสั่งยกเลิก

ขั้นตอนที่ 2: ส่ง signal ไปกับคำขอ API

ต่อไปเราจะนำ signal ไปใช้กับ fetch โดยเพิ่ม option ที่ชื่อ signal ดูตัวอย่างนี้ครับ:

// กำหนด URL ของ API
const url = 'https://api.holysheep.ai/v1/chat/completions';

// กำหนดข้อมูลที่จะส่ง
const data = {
    model: 'gpt-4.1',
    messages: [
        { role: 'user', content: 'สวัสดีชาวโลก' }
    ]
};

// สร้าง AbortController
const controller = new AbortController();

// ส่งคำขอพร้อม signal
fetch(url, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer YOUR_HOLYSHEEP_API_KEY'
    },
    body: JSON.stringify(data),
    signal: controller.signal  // ← บรรทัดสำคัญ!
})
.then(response => response.json())
.then(result => console.log('ผลลัพธ์:', result))
.catch(error => {
    if (error.name === 'AbortError') {
        console.log('คำขอถูกยกเลิกแล้ว');
    } else {
        console.log('เกิดข้อผิดพลาด:', error);
    }
});

ขั้นตอนที่ 3: สร้างปุ่มยกเลิก

ต่อไปเราจะสร้างปุ่มให้ผู้ใช้กดยกเลิกได้ และตั้งเวลายกเลิกอัตโนมัติหลัง 10 วินาที:

<!-- สร้างปุ่มใน HTML -->
<button id="btnSend">ส่งคำขอ</button>
<button id="btnCancel">ยกเลิก</button>
<div id="result"></div>

<script>
// กำหนด URL และข้อมูล
const url = 'https://api.holysheep.ai/v1/chat/completions';
const apiKey = 'YOUR_HOLYSHEEP_API_KEY';

let controller; // ตัวแปรเก็บ Controller

// ฟังก์ชันส่งคำขอ
async function sendRequest() {
    controller = new AbortController();
    const resultDiv = document.getElementById('result');
    
    try {
        resultDiv.innerHTML = 'กำลังโหลด...';
        
        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': Bearer ${apiKey}
            },
            body: JSON.stringify({
                model: 'gpt-4.1',
                messages: [
                    { role: 'user', content: 'เล่าเรื่องตลกขำๆ สักเรื่อง' }
                ]
            }),
            signal: controller.signal
        });
        
        const data = await response.json();
        resultDiv.innerHTML = <strong>ผลลัพธ์:</strong> ${data.choices[0].message.content};
        
    } catch (error) {
        if (error.name === 'AbortError') {
            resultDiv.innerHTML = '❌ คำขอถูกยกเลิกแล้ว';
        } else {
            resultDiv.innerHTML = ❌ เกิดข้อผิดพลาด: ${error.message};
        }
    }
}

// ฟังก์ชันยกเลิก
function cancelRequest() {
    if (controller) {
        controller.abort();
        console.log('สั่งยกเลิกคำขอแล้ว');
    }
}

// ตั้งเวลายกเลิกอัตโนมัติหลัง 10 วินาที
const timeoutId = setTimeout(() => {
    cancelRequest();
}, 10000);

// ผูกปุ่มกับฟังก์ชัน
document.getElementById('btnSend').addEventListener('click', () => {
    clearTimeout(timeoutId); // ยกเลิก timeout เดิมถ้ามี
    sendRequest();
});

document.getElementById('btnCancel').addEventListener('click', () => {
    clearTimeout(timeoutId); // ยกเลิก timeout ก่อน
    cancelRequest();
});
</script>

ประยุกต์ใช้กับ Search Box จริงๆ

ตัวอย่างที่ใช้บ่อยที่สุดคือ Search Box ที่ผู้ใช้พิมพ์แล้วระบบค้นหาอัตโนมัติ เราต้องยกเลิกคำขอเก่าทุกครั้งที่ผู้ใช้พิมพ์ใหม่:

<!-- HTML สำหรับ Search Box -->
<input type="text" id="searchInput" placeholder="พิมพ์คำค้นหา...">
<div id="searchResults"></div>

<script>
const searchInput = document.getElementById('searchInput');
const searchResults = document.getElementById('searchResults');
let searchController; // เก็บ AbortController

searchInput.addEventListener('input', async (event) => {
    const query = event.target.value.trim();
    
    // ถ้าไม่มีข้อความ ไม่ต้องทำอะไร
    if (!query) {
        searchResults.innerHTML = '';
        return;
    }
    
    // ยกเลิกคำขอเดิมก่อนเสมอ
    if (searchController) {
        searchController.abort();
        console.log('ยกเลิกคำขอเก่าแล้ว');
    }
    
    // สร้าง AbortController ใหม่
    searchController = new AbortController();
    
    searchResults.innerHTML = '🔍 กำลังค้นหา...';
    
    try {
        // จำลองการเรียก API (แทนที่ด้วย API จริงของคุณ)
        await new Promise(resolve => setTimeout(resolve, 1000));
        
        // ส่งคำขอจริง (ใช้ HolySheep API)
        /*
        const response = await fetch(
            'https://api.holysheep.ai/v1/chat/completions',
            {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': 'Bearer YOUR_HOLYSHEEP_API_KEY'
                },
                body: JSON.stringify({
                    model: 'gpt-4.1',
                    messages: [
                        { 
                            role: 'user', 
                            content: ค้นหาข้อมูลเกี่ยวกับ: ${query} 
                        }
                    ]
                }),
                signal: searchController.signal
            }
        );
        
        const data = await response.json();
        searchResults.innerHTML = data.choices[0].message.content;
        */
        
        // ผลลัพธ์จำลอง
        searchResults.innerHTML = 📋 ผลการค้นหา: "${query}" - พบ 5 รายการ;
        
    } catch (error) {
        if (error.name === 'AbortError') {
            console.log('ข้ามผลลัพธ์เก่าแล้ว');
        } else {
            searchResults.innerHTML = ❌ เกิดข้อผิดพลาด: ${error.message};
        }
    }
});
</script>

หลักการสำคัญของตัวอย่างนี้คือ: ทุกครั้งที่พิมพ์ใหม่ ให้ยกเลิกคำขอเก่าก่อน นี่จะช่วยประหยัดทรัพยากรและทำให้แอปทำงานเร็วขึ้นมาก

ใช้ AbortController กับ axios

ถ้าโปรเจกต์ของคุณใช้ axios ซึ่งเป็น Library ยอดนิยมสำหรับเรียก API ก็มีวิธีง่ายๆ ครับ:

// ติดตั้ง axios ก่อน: npm install axios

// สร้าง Cancel Token แบบ axios
const controller = new AbortController();
const signal = controller.signal;

// ส่งคำขอด้วย axios
axios.post(
    'https://api.holysheep.ai/v1/chat/completions',
    {
        model: 'gpt-4.1',
        messages: [
            { role: 'user', content: 'ทักทายฉันหน่อย' }
        ]
    },
    {
        headers: {
            'Authorization': 'Bearer YOUR_HOLYSHEEP_API_KEY'
        },
        signal: signal  // หรือ cancelToken: axios.CancelToken.source().token
    }
)
.then(response => {
    console.log('สำเร็จ:', response.data);
})
.catch(error => {
    if (axios.isCancel(error)) {
        console.log('คำขอถูกยกเลิกแล้ว');
    } else {
        console.log('เกิดข้อผิดพลาด:', error.message);
    }
});

// ยกเลิกคำขอเมื่อต้องการ
// controller.abort();

รวมทุกอย่างเป็น Hook สำหรับ React

ถ้าใช้ React การสร้าง Hook สำหรับจัดการคำขอที่ยกเลิกได้จะช่วยให้โค้ดสะอาดมาก:

// useApiRequest.js - Custom Hook สำหรับ React
import { useState, useEffect, useRef } from 'react';

export function useApiRequest() {
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(null);
    const controllerRef = useRef(null);

    const sendRequest = async (url, options = {}) => {
        // ยกเลิกคำขอเก่าถ้ามี
        if (controllerRef.current) {
            controllerRef.current.abort();
        }

        // สร้าง AbortController ใหม่
        controllerRef.current = new AbortController();

        setLoading(true);
        setError(null);

        try {
            const response = await fetch(url, {
                ...options,
                signal: controllerRef.current.signal
            });

            const data = await response.json();
            setLoading(false);
            return data;

        } catch (err) {
            if (err.name === 'AbortError') {
                console.log('คำขอถูกยกเลิก');
                setError('CANCELLED');
            } else {
                setError(err.message);
            }
            setLoading(false);
            return null;
        }
    };

    const cancel = () => {
        if (controllerRef.current) {
            controllerRef.current.abort();
        }
    };

    // ยกเลิกเมื่อ Component ถูกลบออก
    useEffect(() => {
        return () => {
            if (controllerRef.current) {
                controllerRef.current.abort();
            }
        };
    }, []);

    return { sendRequest, cancel, loading, error };
}

// วิธีใช้ใน Component:
// const { sendRequest, cancel, loading, error } = useApiRequest();
// 
// const handleSubmit = async () => {
//     const data = await sendRequest(
//         'https://api.holysheep.ai/v1/chat/completions',
//         {
//             method: 'POST',
//             headers: {
//                 'Authorization': 'Bearer YOUR_HOLYSHEEP_API_KEY'
//             },
//             body: JSON.stringify({ /* ... */ })
//         }
//     );
// };

ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข

กรณีที่ 1: ยกเลิกแล้วแต่ still ได้รับ Error อื่น

ปัญหา: หลังจาก abort แล้ว catch block จับได้แต่เป็น Error ปกติไม่ใช่ AbortError

// ❌ วิธีผิด - ไม่ตรวจสอบชื่อ Error
.catch(error => {
    console.log('เกิดข้อผิดพลาด:', error); // จะแสดง AbortError ด้วย
});

// ✅ วิธีถูก - ตรวจสอบชื่อ Error ก่อน
.catch(error => {
    if (error.name === 'AbortError') {
        console.log('คำขอถูกยกเลิกโดยผู้ใช้');
        // จัดการกรณียกเลิกที่นี่
    } else {
        console.log('เกิดข้อผิดพลาดจริง:', error.message);
        // จัดการ Error อื่นๆ ที่นี่
    }
});

// หรือใช้ instanceOf
.catch(error => {
    if (error instanceof DOMException && error.name === 'AbortError') {
        console.log('ยกเลิกแล้ว ไม่ต้องทำอะไร');
    } else {
        throw error; // Re-throw Error อื่นๆ
    }
});

กรณีที่ 2: ยกเลิกคำขอเดียวแต่ยกเลิกหมดทุกคำขอ

ปัญหา: ใช้ Controller เดียวกันกับหลายคำขอ พอ abort ก็ยกเลิกหมด

// ❌ วิธีผิด - ใช้ Controller เดียวกัน
const controller = new AbortController();

Promise.all([
    fetch(url1, { signal: controller.signal }),
    fetch(url2, { signal: controller.signal }),
    fetch(url3, { signal: controller.signal }),
]);

controller.abort(); // ยกเลิกทั้ง 3 คำขอ!

// ✅ วิธีถูก - สร้าง Controller แยกสำหรับแต่ละคำขอ
const controllers = [];

const request1 = fetch(url1, { 
    signal: (controllers[0] = new AbortController()).signal 
});
const request2 = fetch(url2, { 
    signal: (controllers[1] = new AbortController()).signal 
});
const request3 = fetch(url3, { 
    signal: (controllers[2] = new AbortController()).signal 
});

// ยกเลิกเฉพาะ request2
controllers[1].abort();

// หรือถ้าต้องการยกเลิกพร้อมกันทั้งหมด
controllers.forEach(c => c.abort());

กรณีที่ 3: Memory Leak เพราะไม่ยกเลิกเมื่อ Component ถูกลบ

ปัญหา: ผู้ใช้ออกจากหน้าแล้วแต่คำขอยังทำงานต่อ เกิด Memory Leak

// ❌ วิธีผิด - ไม่มีการ cleanup
function MyComponent() {
    const [data, setData] = useState(null);
    
    const loadData = async () => {
        const controller = new AbortController();
        const response = await fetch(url, { signal: controller.signal });
        const result = await response.json();
        setData(result);
    };
    
    return <div>{data}</div>;
}

// ✅ วิธีถูก - ใช้ useEffect cleanup
function MyComponent() {
    const [data, setData] = useState(null);
    
    useEffect(() => {
        const controller = new AbortController();
        
        const loadData = async () => {
            try {
                const response = await fetch(url, { signal: controller.signal });
                const result = await response.json();
                setData(result);
            } catch (error) {
                if (error.name !== 'AbortError') {
                    console.error(error);
                }
            }
        };
        
        loadData();
        
        // ✅ Cleanup - ยกเลิกเมื่อ Component ถูกลบ
        return () => {
            controller.abort();
        };
    }, []);
    
    return <div>{data}</div>;
}

// สำหรับ React with Custom Hook (อีกวิธี)
function useAbortController() {
    const controllerRef = useRef(null);
    
    useEffect(() => {
        return () => {
            if (controllerRef.current) {
                controllerRef.current.abort();
            }
        };
    }, []);
    
    return controllerRef;
}

กรณีที่ 4: ส่ง API Key ไม่ถูกต้อง

ปัญหา: ได้รับ Error 401 Unauthorized เพราะวิธีส่ง API Key ผิด

// ❌ วิธีผิด - ส่ง API Key ใน URL หรือผิด format
fetch('https://api.holysheep.ai/v1/chat/completions?key=YOUR_KEY');
fetch(url, { 
    headers: { 'api-key': apiKey } // ชื่อ Header ผิด!
});

// ✅ วิธีถูก - ส่ง Bearer Token ใน Authorization Header
const response = await fetch('https://api.holysheep.ai/v1/chat/completions', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': Bearer ${apiKey}  // ต้องมี "Bearer " นำหน้า!
    },
    body: JSON.stringify({
        model: 'gpt-4.1',
        messages: [
            { role: 'user', content: 'สวัสดี' }
        ]
    }),
    signal: controller.signal
});

const data = await response.json();

สรุป

วันนี้เราได้เรียนรู้วิธีใช้ AbortController เพื่อยกเลิกคำขอ API กันแล้วครับ สิ่งสำคัญที่ต้องจำคือ:

การใช้ AbortController อย่างถูกต้องจะช่วยให้แอปพลิเคชันของคุณทำงานเร็วขึ้น ประหยัดทรัพยากร และให้ประสบการณ์ที่ดีกับผู้ใช้ครับ

แนะนำ HolySheep AI

ถ้าคุณกำลังมองหาผู้ให้บริการ AI API ที่มีความเร็วสูงและราคาประหยัด HolySheep AI เป็นตัวเลือกที่ยอดเยี่ยมครับ:

ราคา AI ในปี 2026 (ต่อล้าน Token):

ลองนำโค้ดจากบทความนี้ไปประยุกต์ใช้กับ API ของ HolySheep ดูนะครับ รับรองว่าคุ้มค่าแน่นอน!

👉 สมัคร HolySheep AI — รับเครดิตฟรีเมื่อลงทะเบียน