คำสั่ง async/await ในภาษา JavaScript
ในบทนี้ คุณจะได้เรียนรู้การใช้งานคำสั่ง async/await ในภาษา JavaScript ที่ใช้สำหรับเปลี่ยนการทำงานของ Promise ให้เป็นแบบ Synchronous ซึ่งมันสามารถช่วยลดการใช้งานของฟังก์ชัน Callback และทำให้โค้ดอ่านง่ายและเป็นระเบียบมากขึ้น นี่เป็นเนื้อหาในบทนี้
- Async function
- การใช้งานคำสั่ง Await
- Handing promise rejection
- ตัวอย่าการใช้งานคำสั่ง async/await
Async function
คำสั่ง async
เป็นคำสั่งที่ใช้กำหนดให้กับฟังก์ชันในภาษา JavaScript เพื่อบ่งบอกว่าฟังก์ชันส่งค่ากลับเป็น Promise ซึ่งเป็นออบเจ็คของการทำงานแบบ Asynchronous ดังนั้นการที่ฟังก์ชันถูกประกาศด้วยคำสั่ง async
จะทำให้มันเป็นฟังก์ชัน Asynchronous โดยปริยาย นี่เป็นรูปแบบการใช้งาน
async function fn() {
return promise;
}
ในรูปแบบการใช้งาน เราระบุคำสั่ง async
หน้าฟังก์ชันในตอนที่ประกาศเพื่อทำให้มันเป็นฟังก์ชัน Asynchronous โดยค่าที่ส่งกลับจากฟังก์ชันจะต้องเป็นออบเจ็คของ Promise เสมอ และมันยังสามารถใช้กับ Arrow function ได้เช่นกัน
const fb = async () => {
return promise;
}
นี่ให้ผลลัพธ์การทำงานที่เหมือนกัน แต่เราใช้ Arrow function ในการประกาศฟังก์ชันแทน นั่นหมายความว่าคุณสามารถเลือกใช้แบบไหนก็ได้ที่ต้องการ
อย่างที่ได้บอกไปก่อนหน้า ฟังก์ชันที่เป็น async จะต้องส่งค่ากลับเป็น Promise ออบเจ็คเสมอ ซึ่งนี่เป็นข้อบังคับ ในตัวอย่างนี้ เป็นการประกาศฟังก์ชันที่ส่งค่ากลับเป็น Promise ที่ resolve ค่าเป็นข้อความ "Hello"
async function myFunction() {
return new Promise((resolve, reject) => {
resolve('Hello');
});
}
// Or
async function myFunction() {
return Promise.resolve('Hello');
}
และนอกจากนี้ คุณยังสามารถละเว้นการสร้างออบเจ็ค Promise ที่ใช้ในการ resolve ค่าได้ นั่นคือเพียงแค่ส่งค่าที่ต้องการจากฟังก์ชันได้ทันที ยกตัวอย่างเช่น
async function myFunction() {
return 'Hello';
}
ในกรณีนี้ เมื่อข้อมูลที่ส่งกลับจากฟังก์ชันไม่ใช่ Promise ภาษา JavaScript จะสร้าง Promise ที่ resolve ค่าดังกล่าวให้กับเราอัตโนมัติ ดังนั้นการส่งค่ากลับทั้งสามรูปแบบก่อนหน้าได้ผลลัพธ์เป็น Promise ที่ resolve ค่าเป็น "Hello"
เช่นเดียวกัน นั่นหมายความว่าในการประกาศฟังก์ชันด้วยคำสั่ง async
จะช่วยให้เราแน่ใจได้ว่าฟังก์ชันจะส่งค่ากลับเป็น Promise เสมอ
ตัวอย่างต่อมา เป็นการประกาศและใช้งานฟังก์ชัน Async ที่การทำงานมีความหมายมากขึ้น เรามาเขียนฟังก์ชัน Async สำหรับกล่าวคำทักทายชืื่อที่ส่งเข้ามาในฟังก์ชันหลังจากที่เวลาผ่านไป 1 วินาที นี่เป็นโค้ดของโปรแกรม
async function greet(name) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`Hello ${name}!`);
}, 1000);
});
}
greet('Metin').then((value) => {
console.log(value);
});
นี่เป็นผลลัพธ์การทำงานของโปรแกรม
Hello Metin!
ฟังก์ชัน greet
เป็นฟังก์ชัน Async ที่ส่งค่ากลับเป็นคำกล่าวทักทายชื่อที่ส่งเข้ามายังฟังก์ชันเมื่อเวลาผ่านไป 1 วินาที จะเห็นว่าฟังก์ชันส่งค่ากลับเป็นออบเจ็ค Promise ที่เรียกใช้งานฟังก์ชัน setTimeout
เพื่อหน่วงเวลาการทำงานของโปรแกรม และทำการ resolve ค่าหลังจากที่เวลาผ่านไป 1 วินาที
greet('Metin').then((value) => {
console.log(value);
});
เนื่องจากค่าที่ส่งกลับจากฟังก์ชันเป็น Promise นั่นทำให้เราเรียกใช้เมธอด then
เพื่อรับเอาค่าที่ resolve มาใช้งานได้และแสดงมันออกทางหน้าจอ ส่วนในกรณีที่ Promise มีการ reject คุณสามารถใช้เมธอด catch
เพื่อจัดการกับข้อผิดพลาดที่เกิดขึ้นได้ ซึ่งนี่ก็เป็นการทำงานพื้นฐานของ Promise อยู่แล้ว
และสิ่งหนึ่งที่ดีเกี่ยวกับ Promise ที่จะพูดถึงในบทนี้ก็คือมันสามารถใช้ร่วมกับคำสั่ง await
เพื่อเปลี่ยนการทำงานของ Promise ให้เป็นแบบ Synchronous ได้ นี่สามารถลดการใช้งานฟังก์ชัน Callback และทำให้โค้ดอ่านง่ายและเป็นระเบียบมากขึ้น
การใช้งานคำสั่ง Await
คำสั่ง await
ใช้เพื่อเปลี่ยนการทำงานของ Promise ให้เป็นแบบ Synchronous หรือบล็อคการทำงานจนกว่า Promise จะทำงานเสร็จ (resolve หรือ reject) นี่เป็นรูปแบบใหม่ที่จะสามารถช่วยให้การเขียนโปรแกรมกับ Promise สะดวกและง่ายขึ้นโดยที่ไม่ต้องใช้ฟังก์ชัน Callback
นี่เป็นรูปแบบของการใช้งานคำสั่ง await
ในภาษา JavaScript
let result = await promise;
// Or
let result = await asyncFn();
ในรูปแบบการใช้งาน เราสามารถใช้คำสั่ง await
กับออบเจ็ค Promise หรือฟังก์ชัน Async ที่ส่งค่ากลับเป็น Promise ได้ นี่จะทำให้โปรแกรมรอจนกว่า Promise จะทำงานเสร็จสิ้น และค่าที่ resolve จาก Promise จะถูกส่งกลับโดยตรงจากการเรียกใช้ฟังก์ชัน
นั่นหมายความว่าเราไม่จำเป็นต้องใช้เมธอด then
เพื่อรับค่าจาก Promise อีกต่อไป เพียงแค่ใช้คำสั่ง await
หน้าฟังก์ชัน และรอรับค่าที่ resolve มาจาก Promise เหมือนกับที่การเรียกใช้ฟังก์ชันปกติที่มีการทำงานเป็นแบบ Synchronous อยู่แล้ว
และจากตัวอย่างก่อนหน้าของโปรแกรมกล่าวทักทายชื่อ สามารถนำมาเขียนใหม่ได้ด้วยการใช้คำสั่ง await
นี่เป็นตัวอย่าง
async function greet(name) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`Hello ${name}!`);
}, 1000);
});
}
async function run() {
let message = await greet('Metin');
console.log(message);
}
run();
นี่เป็นผลลัพธ์การทำงานของโปรแกรม
Hello Metin!
ในตัวอย่างนี้ เป็นโปรแกรมสำหรับกล่าวคำทักทายหลังจากเวลาผ่านไป 1 วินาทีเช่นเดิม แต่แทนที่จะใช้เมธอด then
ของ Promise เพื่อรอรับค่าส่งกลับ เราใช้คำสั่ง await
แทน จะเห็นว่าการทำงานของฟังก์ชัน greet
จะเหมือนกับว่ามันเป็น Synchronous และใช่แล้วมันเป็น
let message = await greet('Metin');
ในบรรทัดนี้จะทำให้โปรแกรมรอจนกว่า Promise จะส่งค่ากลับ เราเพียงแค่ระบุคำสั่ง await
หน้าฟังก์ชันที่เรียกใช้ และค่าที่ resolve จาก Promise จะถูกส่งกลับมาจากการเรียกใช้นี้ และถูกเก็บลงในตัวแปร message
อย่างไรก็ตาม เมื่อฟังก์ชันเป็น Async คุณต้องไม่ลืมที่จะใส่คำสั่ง await
หน้าการเรียกใช้งานฟังก์ชันด้วยเสมอ ไม่เช่นนั้น ค่าที่ส่งกลับจะเป็น Promise จะไม่ใช่ค่าที่ resolve จาก Promise ยกตัวอย่างเช่น ถ้าหากคุณลืม
let message = greet('Metin');
และนี่เป็นผลลัพธ์การทำงานของโปรแกรม
Promise { <pending> }
จะเห็นว่าค่าที่ได้รับจากการเรียกใช้ฟังก์ชันโดยไม่ระบุคำสั่ง await
จะเป็น Promise แทน และนี่เป็นการทำงานปกติสำหรับการส่งค่ากลับของฟังก์ชันในภาษา JavaScript ดังนั้นการใช้คำสั่ง await
จะเปลี่ยนวิธีการทำงานนี้และรอการผลจาก Promise แทน
หรือกล่าวอีกนัยหนึ่ง await
ถูกออกแบบให้ใช้ร่วมกับฟังก์ชันที่ถูกประกาศด้วยคำสั่ง async
ที่ส่งค่ากลับเป็น Promise ออบเจ็คเพื่อทำให้มันรอการทำงานจนกว่า Promise จะ resolve ค่ากลับ
อีกสิ่งหนึ่งที่สำคัญในการใช้งานคำสั่ง await
ก็คือ เราได้สร้างฟังก์ชัน run
ซึ่งเป็นฟังก์ชัน Async ที่ใช้สำหรับรันโปรแกรม เหตุผลที่ต้องทำเช่นนี้เพราะนี่เป็นข้อบังคับในภาษา JavaScript เนื่องจากว่าคำสั่ง await
นั้นจะสามารถใช้ได้ในฟังก์ชัน Asynchronous หรือฟังก์ชันที่ถูกประกาศด้วยคำสั่ง async
เท่านั้น
นั่นหมายความว่าคุณไม่สามารถใช้คำสั่ง await
ได้ในระดับ Top level ของโปรแกรม มีิอีกวิธีที่เราสามารถใช้งานคำสั่ง await
ได้อย่างรวดเร็วคือการสร้างฟังก์ชัน Anonymous ที่เป็นแบบ Async ครอบทั้งหมดของโปรแกรม ยกตัวอย่างเช่น
(async function () {
// Now can use 'await' here
})();
Handing promise rejection
Promise ไม่เพียงแค่ส่งค่ากลับเป็นผลสำเร็จ (resolve) เท่านั้่น แต่มันยังสามารถส่งค่ากลับเป็นผลล้มเหลวได้ นั่นก็คืือ Promise ที่ถูก Reject นั่นเอง ซึ่งการ Reject สามารถเกิดได้จากข้อผิดพลาดใดๆ ที่เกิดขึ้นใน Promise ที่เกิดจากการ throw error
หรือจากการเรียกใช้ฟังก์ชัน reject
โดยตรง
เมื่อเกิดข้อผิดพลาดขึ้น ในการจัดการด้วยวิธีของ Promise ปกตินั้นสามารถทำได้โดยการใช้งานเมธอด catch
เพิื่อกำหนดฟังก์ชัน Callback สำหรับตรวจสอบและจัดการกับข้อผิดพลาด ยกตัวอย่างเช่น
myPromiseFn().then((value) => {
// Done
}).catch((err) => {
// Handing error
});
แต่เมื่อใช้คำสั่ง await
กับ Promise ในการจัดการกับข้อผิดพลาดทำให้เราสามารถใช้คำสั่ง try catch ได้เลย เนื่องจากนี่เป็นการทำงานแบบ Synchronous ยกตัวอย่างเช่น
try {
let value = await myPromiseFn();
// Done
} catch (err) {
// Handing error
}
ซึ่งง่ายกว่าที่คุณคิด นี่เป็นวิธีจัดการกับข้อผิดพลาดที่คุ้นเคยในภาษา JavaScript เนื่องจากจากโค้ดทำงานแบบ Synchronous แล้วนั่นเอง ดังนั้นสรุปได้ว่าการใช้คำสั่ง await
ช่วยให้ไม่ต้องใช้ฟังก์ชัน then
และ catch
ของ Promise อีกต่อไป
ตัวอย่างการใช้งานคำสั่ง async/await
สำหรับตัวอย่างการใช้งานคำสั่ง async/await
ในรูปแบบเต็ม จะเป็นการใช้งานแพ็กเกจ node-fetch
สำหรับเรียกข้อมูลผ่านทาง HTTP เพื่อนำมาใช้งานในโปรแกรม ก่อนอื่นคุณจะต้องติดตั้งแพ็กเกจด้วย npm โดยคำสั่งต่อไปนี้
npm install node-fetch
นี่เป็นโมดูลที่ทำงานเหมือนกับ window.fetch
API บนเว็บเบราว์เซอร์ แต่เนื่องจากบทเรียนนี่เป็น Node.js เราจึงต้องติดตั้งมันผ่าน npm ในตัวอย่างนี้ เราจะเขียนโปรแกรมเพื่อเรียกเอาข้อมูลจาก API นี้บนเว็บไซต์ Github: https://api.github.com/users/github
หากคุณมีบัญชีของคุณบน Github คุณสามารถเปลี่ยน API ให้รับข้อมูลบัญชีของคุณแทนโดยแทนที่ชื่อผู้ใช้เป็นของคุณเอง เช่น
https://api.github.com/users/{username}
โดย API นี้ใช้สำหรับรับเอาข้อมูลเกี่ยวกับบัญชีของ Github เอง เราสามารถใช้ฟังก์ชัน fetch
เพื่อเรียกเอาข้อมูลจาก API ได้ดังนี้
const fetch = require('node-fetch');
fetch('https://api.github.com/users/github')
.then(res => res.json())
.then(json => console.log(json));
เมื่อคุณรันโปรแกรมผลลัพธ์จะแสดงเป็นจำนวนมาก นั่นเป็นข้อมูลทั้งหมดที่ได้จากการเรียกใช้งาน API และนี่เป็นเพียงผลลัพธ์บางส่วน
{
login: 'github',
id: 9919,
node_id: 'MDEyOk9yZ2FuaXphdGlvbjk5MTk=',
avatar_url: 'https://avatars.githubusercontent.com/u/9919?v=4',
gravatar_id: '',
...
ฟังก์ชัน fetch
เป็นฟังก์ชัน Asynchronous ที่ส่งค่ากลับเป็น Promise นั่นหมายความว่าเมื่อการเชื่อมต่อไปยัง Server สำเร็จ มันส่งค่ากลับเป็นออบเจ็คของ Promise ที่ resolve ค่าเป็นออบเจ็ค Response สำหรับการร้องขอ
.then(res => res.json())
ดังนั้นเราเรียกใช้เมธอด then
เพื่อรับเอาออบเจ็คมาใช้งาน และในตอนนี้ค่าที่ส่งกลับมาจากออบเจ็ค Response นั้นมีเพียง Header เท่านั้น นั่นหมายความว่าเพื่ออ่านค่าของ Response body เราจะต้องเรียกใช้อีกเมธอดบนออบเจ็ค นั่นคือเมธอด res.json
.then(json => console.log(json));
เมธอด res.json
จะส่งค่ากลับเป็น Promise ที่ resolve ข้อมูลของ Response body ในรูปแบบ JSON และเราเรียกใช้เมธอด then
อีกครั้งเพื่อรับค่าสุดท้าย จะเห็นว่าเราได้ทำการเชื่อมต่อ Promise เนื่องจากมีมากกว่าหนึ่ง Promise ที่ทำงานต่อเนื่องกัน นั่นคือการเรียกใช้ฟังก์ชัน fetch
และตามด้วยเมธอด res.json
แทนที่จะใช้เมธอด then
และ catch
และอย่างที่คุณรู้ เราสามารถใช้คำสั่ง await
แทนได้ ดังนั้นในตัวอย่างนี้ เราสามารถใช้คำสั่ง async/await
แทนได้ดังนี้
const fetch = require('node-fetch');
(async function () {
// wait for http response
let res = await fetch('https://api.github.com/users/github');
// wait until response body is read
let json = await res.json();
// finally got value
console.log(json)
})();
นี่ให้ผลลัพธ์เหมือนกับตัวอย่างก่อนหน้า แต่ในตัวอย่างนี้ เราใช้คำสั่ง async/await
แทน สังเกตว่าโค้ดอ่านง่ายขึ้นเนื่องจากมันทำงานเป็นลำดับโดยไม่มีฟังก์ชัน Callback และนี่เป็นวัตถุประสงค์ของคำสั่ง await
ที่ถูกออกแบบมา เพื่อทำให้การเขียนโปรแกรมกับ Promise ง่ายขึ้น
และในกรณีที่คุณต้องการรันโปรแกรมนี้บนเว็บเบราวน์เซอร์ เพีียงแค่ลบบรรทัดนี้ออก
const fetch = require('node-fetch');
จากนั้นบันทึกไฟล์และรันมันบนเบราวน์เซอร์ คุณไม่จำเป็นต้องติดตั้งโมดูล node-fetch
เพื่อใช้งานเนื่องจากฟังก์ชัน fetch
สามารถใช้ได้บนเบราวน์เซอร์อยู่แล้ว และคุณสามารถดูผลลัพธ์การทำงานได้ที่ Console ของเว็บเบราว์เซอร์
ในบทเรียนนี้ คุณได้เรียนรู้เกี่ยวกับคำสั่ง async/await ที่ออกแบบมาสำหรับใช้งานร่วมกับ Promise เพื่อเปลี่ยนการทำงานเป็นแบบ Synchronous นี่ช่วยให้การทำงานกับ Promise สามารถทำได้ง่ายขึ้นในกรณีที่มันมีการทำงานต่อเนื่องกันแบบเป็นลำดับ