Async, Promise & AJAX

JavaScript ไม่รอฟังก์ชันบางประเภททำงานเสร็จ — โค้ดบรรทัดถัดไปทำงานต่อทันที ทำให้ลำดับผลลัพธ์ไม่เป็นไปตามที่เขียน

ปัญหาของ asynchronous

setTimeout(function () {
    console.log(1);
}, 5000);
console.log(2);

// ผลลัพธ์: 2 ออกก่อน แล้ว 1 ตามมาอีก 5 วินาที

ตัวอย่าง 3 งานจากสไลด์

function taskOne()   { setTimeout(function(){ console.log("this is task 1"); }, 500); }
function taskTwo()   { console.log("this is task 2"); }
function taskThree() { setTimeout(function(){ console.log("this is task 3"); }, 1000); }

taskOne();      // รอ 500ms
taskTwo();      // ไม่รอ
taskThree();    // รอ 1000ms

ผลลัพธ์: task 2 → task 1 → task 3 (ไม่ใช่ 1, 2, 3 ตามที่เขียน)

เวลา taskOne task 1 (500ms) taskTwo task 2 ทันที taskThree task 3 (1000ms) เรียกตามลำดับ 1 → 2 → 3 แต่ผลลัพธ์ออกมาเป็น 2 → 1 → 3 เพราะ setTimeout ไม่บล็อกโค้ดบรรทัดถัดไป

นี่คือเหตุผลที่ต้องมี Promise และ async/await

ฟังก์ชันไหนเป็น asynchronous บ้าง

Promise

const myPromise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("สำเร็จ");     // เรียก resolve เมื่อทำงานเสร็จ
        // reject("ล้มเหลว");   // เรียก reject เมื่อผิดพลาด
    }, 200);
});

myPromise
    .then(result => console.log(result))
    .catch(error => console.error(error));
สถานะของ Promiseความหมาย
pendingกำลังทำงาน ยังไม่รู้ผล
fulfilledสำเร็จ — เรียก resolve() แล้ว รับผลด้วย .then()
rejectedล้มเหลว — เรียก reject() แล้ว รับผลด้วย .catch()

async / await

ทำให้ฟังก์ชัน asynchronous ทำงานเรียงลำดับได้ตามต้องการ

async function taskOne() {
    return new Promise((resolve) => {
        setTimeout(function () {
            console.log("this is task 1");
            resolve(true);
        }, 3000);
    });
}
async function taskTwo() {
    return new Promise(resolve => {
        setTimeout(function () {
            console.log("this is task 2");
            resolve(true);
        }, 2000);
    });
}

async function test() {
    await taskOne();     // รอให้เสร็จก่อน
    await taskTwo();     // แล้วค่อยทำตัวถัดไป
}
test();
กฎที่ต้องจำ: await ใช้ได้เฉพาะใน function ที่ประกาศว่า async เท่านั้น และฟังก์ชันที่ await ต้องคืน Promise
resolve จะคืนค่าอะไรก็ได้ — ค่าที่ใส่ใน resolve() คือค่าที่ await หรือ .then() จะได้รับ

Promise.any (ES10)

const myPromise1 = new Promise((resolve, reject) => {
    setTimeout(() => { resolve("queen"); }, 200);
});
const myPromise2 = new Promise((resolve, reject) => {
    setTimeout(() => { reject(); }, 100);
});

Promise.any([myPromise1, myPromise2]).then((x) => {
    console.log(x);          // "queen" — เอาตัวที่ resolve สำเร็จเร็วที่สุด
});
// ถ้าล้มเหลวทุกตัว
const a = Promise.reject('error A');
const b = Promise.reject('error B');
Promise.any([a, b])
    .then(result => console.log(result))
    .catch(error => console.error('ทุกตัวล้มเหลว:', error));
ใช้ตอนไหน: เช่นมี cluster server หลายเครื่องที่ให้บริการเหมือนกัน ยิงพร้อมกันแล้วเอาตัวที่ตอบเร็วสุดไปใช้งานต่อ

AJAX คืออะไร

Asynchronous JavaScript and XML = การร้องขอข้อมูลไปยัง server โดยไม่ต้อง refresh หน้าเว็บ

XMLHttpRequest

function loadDoc() {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            document.getElementById("demo").innerHTML = this.responseText;
        }
    };
    xhttp.open("GET", "ajax_info.txt", true);   // true = asynchronous
    xhttp.send();
}
readyStateชื่อความหมาย
0UNSENTสร้าง object แล้ว แต่ยังไม่เปิดการเชื่อมต่อ
1OPENEDเรียก open() แล้ว ยังไม่ได้ send()
2HEADERS_RECEIVEDเรียก send() แล้ว เริ่มรับ header
3LOADINGกำลังรับข้อมูลจาก server
4DONEเสร็จสมบูรณ์ — ใช้คู่กับ status == 200
// ส่งแบบ GET / POST
xhttp.open("GET", "demo_get.asp", true);
xhttp.send();

xhttp.open("POST", "demo_post.asp", true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.send("fname=Henry&lname=Ford");
ปัจจุบันนิยมใช้ fetch() แทน XMLHttpRequest เพราะรองรับ Promise และ async/await ได้เลย