Promise นั้นสำคัญไฉน

Peerapat Sungkasem
2 min readJul 28, 2017

--

บทความนี้จะมาอธิบายเรื่อง Promise ใน JavaScript ด้วยตัวอย่างแบบง่ายๆกัน

หมายเหตุ: บทความนี้เป็นการอธิบายจากความเข้าใจส่วนตัว อาจมีบางอย่างคลาดเคลื่อนจาก official document สำหรับใครที่ต้องการศึกษาเพิ่มเติมสามารถดูได้จาก Promise — JavaScript | MDN

สิ่งเล็กๆที่เรียกว่า callback

ก่อนจะอธิบายเรื่อง Promise เราต้องทำความรู้จักกับ callback กันก่อน สมมติว่าเรามี funtion random ตัวเลขหน้าตาแบบนี้

function random(callback) {
setTimeout(() => {
callback(Math.random());
}, Math.random() * 1000);
}

ถ้าเราต้องการให้ print ค่าที่ random ออกมาก็ไม่ยาก แค่ส่ง callback function เข้าไปแบบนี้

random(value => {
console.log('Random:', value);
});

ทีนี้ถ้าต้องการ random 3 รอบ แล้วให้ print เรียงกันออกมาล่ะ?

เนื่องจาก JavaScript มันทำงานแบบ asynchronous และการ random แต่ละครั้งใช้เวลาไม่เท่ากัน อันไหนเสร็จก่อนก็ print ก่อน ถ้าอยากให้ print เรียงกันสวยๆเราก็เลยต้องซ้อน callback เข้าไปแบบนี้

random(value1 => {
random(value2 => {
random(value3 => {
console.log('Round 1:', value1);
console.log('Round 2:', value2);
console.log('Round 3:', value3);
});
});
});

OMG! นี่มัน callback hell ในตำนานนี่นา และในความเป็นจริง code ใน callback นั้นไม่ได้มีแค่ console.log แต่ซับซ้อนกว่านั้น การเขียน callback ซ้อนกันหลายๆชั้นจะทำให้ code อ่านยาก Promise จึงถือกำเนิดขึ้นด้วยเหตุนี้

Promise มันคืออะไรกัน ?

อธิบายง่ายๆ Promise ก็คือ object ชนิดหนึ่ง มีได้ 3 สถานะคือ pending, fulfilled และ rejected โดยเมื่อมันเริ่มทำงาน สถานะจะเป็น pending หลังจากทำงานเสร็จสถานะจะเปลี่ยนเป็น fulfilled หรือ rejected ขึ้นอยู่กับเงื่อนไขในการทำงาน

ยังไม่เข้าใจอยู่ดี

งั้นมาดู code ดีกว่า เราจะปรับ function random จากเดิมที่รับ callback ให้ return Promise ออกมาแบบนี้

function random() {
return new Promise((resolve, reject) => {
try {
setTimeout(() => {
resolve(Math.random());
}, Math.random() * 1000);
} catch (err) {
reject(new Error('Oops!'));
}
});
}

จะเห็นว่าเวลาที่เราสร้าง Promise ขึ้นมาจะต้องส่ง function เข้าไปใน constructor โดย function นี้จะเป็นการทำงานของ Promise โดยทั่วไปเมื่อทำงานสำเร็จเราจะส่งผลลัพธ์ออกไปด้วย funtion resolve แต่ถ้าไม่สำเร็จเราจะเรียก function reject

ทีนี้มาลองเรียกใช้ random ตัวใหม่ดู หน้าตา code ก็จะเปลี่ยนไปเป็นแบบนี้

let promise = random();
promise.then(value1 => {
console.log('Round 1:', value1);
return random();
}).then(value2 => {
console.log('Round 2:', value2);
return random();
}).then(value3 => {
console.log('Round 3:', value3);
}).catch(err => {
console.error(err);
});

จะเห็นว่า callback hell หายไป เปลี่ยนเป็นการเรียกใช้ promise.then() ต่อกันเป็น chain แทน โดย function ที่ส่งให้ promise.then() จะทำงานเมื่อสถานะของ promise เป็น fullfilled แต่ถ้าสถานะเป็น rejected ก็จะไปเรียก function ที่ส่งให้ promise.catch()

แต่ที่เจ๋งกว่านั้น เราสามารถทำหลายๆ Promise พร้อมกันแล้วค่อยเอาผลลัพธ์มา print ทีเดียวแบบนี้ก็ได้ ด้วย Promise.all()

let promises = Promise.all([random(), random(), random()]);
promises.then(results => {
let [value1, value2, value3] = results;
console.log('Round 1:', value1);
console.log('Round 2:', value2);
console.log('Round 3:', value3);
}).catch(err => {
console.error(err);
});

จะเห็นว่า code ดูอ่านง่ายขึ้นเยอะเลย

แล้ว async / await ล่ะ ?

ตอนแรกตั้งใจว่าจะเขียนอธิบาย async/await ไปด้วยเลย แต่เดี๋ยวจะยาวไป เลยขอติดไว้ก่อน ไว้มาต่อกันคราวหน้าครับ …

References:

--

--