Java Script Callback ก็คือ การเรียกฟังก์ชั่นแบบ Asynchronous
เอ่ะ แล้วการเรียกแบบ Asynchronous มันคืออะไรอีกเนี่ย ?
คำว่าAsynchronous กับ Synchronous ในแวดวงวิศวกรรมหรือเทคโนโลยีมักจะได้ยินกันบ่อยๆ ซึ่งมักใช้เกี่ยวกับการรับส่ง ข้อมูล สัญญาณ ในรูปแบบต่างๆ ซึ่งการส่งข้อมูลหรือสัญญาณต่างๆนั้นก็
ต้องมี ผู้ร้องขอ – ผู้ตอบกลับ
การส่งแบบ Synchronous
นั้นเป็นการรอจังหวะให้การร้องขอและการตอบกลับเสร็จเป็นเรื่องๆไปจึงจะทำงานตามขั้นตอนการร้องขอครั้งใหม่
การร้องขอครั้งที่ 2 จะเกิดขึ้นได้ก็ต่อเมื่อ การร้องขอครั้งแรกได้รับการตอบกลับแล้ว ซึ่งหากผู้ตอบสนองทำงานได้ช้า ก็จะเกิดปัญหาคอขวด (Bottleneck)เกิดขึ้น
ซึ่งในเหตุการณ์ชีวิตจริง ก็เปรียบได้กับการไปสั่งกาแฟในร้านที่ไม่มีบัตรคิว และไม่จดเมนูให้ หากผู้ร้องขออยากได้กาแฟก็ต้องรอจนถึงรอบตัวเองเท่านั้นถึงจะได้สั่งและรอต่อไป
เปรียบเทียบกับการเขียนโปรแกรม
คำสั่งที่ 1 getUserInfo
คำสั่งที่ 2 getProductList
* คำสั่ง getProductList จะไม่เกิดขึ้นเลยถ้า getUserInfo ยังไม่เสร็จ
function getUserInfo(){
for(var i=0;i<10;i++){
console.log('User Info no ' + i);
}
}
function getProductList(){
for(var i=0;i<10;i++){
console.log('Product no ' + i);
}
}
getUserInfo();
getProductList();
การส่งแบบ Asynchronous
การส่งแบบ Asynchronous ก็จะตรงกันข้ามกับ Synchronous คือ สามารถส่งคำร้องขอไปได้เรื่อยๆ ไม่ต้องรอให้คำขอก่อนหน้าเสร็จก่อน ซึ่งในชีวิตจริง ก็เหมือนการเดินเข้าไปสั่งอาหารในร้าน Junk Food หรือ ร้านกาแฟ ที่มีการบริหารจัดการเรื่องคิว รับออเดอร์ลูกค้ามาก่อน ยังไม่จำเป็นต้องทำเสร็จ ก็รับออเดอร์ถ้ดไปได้เลย ก็จะทำให้ไม่เสียโอกาสในการให้บริการลูกค้าท่านอื่นๆ ซึ่งการเขียนโปรแกรมแบบ Asynchronous ในปัจจุบันก็มาพร้อมการบัญญัติศัพท์ใหม่ๆให้ดูเท่ห์ คือคำว่า None Blocking I/O
ในการเขียนโปรแกรมติดต่อฝั่งเซิร์ฟเวอร์ในงานจริง หากฟังก์ชั่น getUserInfo และ getProductList เป็นการร้องขอไปยังเครื่องเซิร์ฟเวอร์คนละเครื่อง หรือเซิร์ฟเวอร์เครื่องเดียวกัน แต่ผู้ใช้งานอยู่คนละประเทศ ฟังก์ชั่น getProductList ก็อาจทำงานเสร็จก่อน getUserInfo ได้ ซึ่งผู้พัฒนาต้องเข้าใจลำดับการทำงานของโปรแกรมแบบ Asynchronous เพื่อนำผลลัพธ์ที่ได้ไปใช้อย่างถูกต้อง
function getUserInfo(callback){
setTimeout(function(){
callback('got user info!');
},500);
}
function getProductList(callback){
setTimeout(function(){
callback('got product list!');
},100);
}
getUserInfo(function(result){
console.log(result);
});
getProductList(function(result){
console.log(result);
});
จากตัวอย่าง เรียงลำดับการเรียงฟังก์ชั่น getUserInfo ตามด้วย getProductList แต่เอ่ะทำไม getProductList เสร็จก่อนละ ในตัวอย่างนั้นเงื่อนไขชัดเจนคือกำหนดเวลา Timeout ไว้ แต่ในชีวิตจริงการเขียนโปรแกรมแบบ Asynchronous ติดต่อ Server และฐานข้อมูล เป็นเรื่องที่คาดเดาได้ยากว่าอันไหนจะเสร็จก่อน หากวางโค้ดตามลำดับแบบนี้ ถ้าในโปรแกรมต้องให้ UserInfo เสร็จก่อนถึงทำ getProductList รับรองว่าลำดับการทำงานของโปรแกรมผิดแน่ๆ
รูปแบบการประกาศฟังก์ชั่นแบบ Callback
function name(option,callback){
callback(result);
}
- callback – ใช้คืนค่าผลลัพธ์จากการ callback สามารถเขียนเป็นอะไรก็ได้ตามที่ระบุไว้ในตอนรับค่ามาเช่น function a(opt,f){f(result);}
- callback() – ส่งค่าผลลัพธ์กลับไปยังฟังก์ชั่นที่เรียก
- result – ผลลัพ์ธ์ที่ส่งกลับไป สามารถส่งเป็นรูปแบบ json ฟังก์ชั่น variable ต่างๆที่ javascript รองรับ
การเรียกใช้
- name(function(result){
});
- result – ผลลัพธ์ที่ได้รับจากการ callback จะตั้งชื่ออะไรก็ได้ที่ตรงหลักการของ JavaScript ไม่ตรงกับคำสงวน ไม่ตรงกับชื่อตัวแปรที่เป็น global variable
แล้ว callback มันไม่ดียังงัยละ
สรรพสิ่งบนโลกนี้ล้วนอนิจจัง ไม่มีอะไรสมบูรณ์แบบ เมื่อเราแก้ปัญหาอีกอย่าง เราก็จะเจอปัญหาอีกอย่าง สำหรับ callback สิ่งที่นักพัฒนาเจอปัญหาเท่าที่ทราบน่าจะมี ดังนี้
- หากลืมเขียน callback() เพื่อส่งผลลัพธ์กลับ ก็จะไม่ทราบว่าโปรแกรมทำงานถึงไหน เงียบหายไปเลย อันนี้ส่วนใหญ่เป็นข้อผิดพลาดของนักพัฒนาเอง
- หากต้องการผลลัพธ์ตามลำดับขั้น การเขียนฟังก์ชั่นที่ซับซ้อน callback ช่วยให้คุณปวดหัวได้มากขึ้นแน่นอน ที่เราเรียกว่า Callback Hell
ตัวอย่าง
login(function(userInfo){
getMenu(userInfo,function(menuList){
genMenu(menuList,function(status){
showDefaultPage(status,function(){
});
log(userInfo,status);
})
ขั้นตอนการทำงาน
- เมื่อ login เสร็จได้ข้อมูล userInfo
- ทำการดึงข้อมูล menu ที่ผู้ใช้งานมีสิทธิเข้าถึงได้
- genMenu ทำการสร้างเมนูบนหน้าจอ
- ทำการแสดงผลหน้าที่กำหนดเริ่มต้น
- ทำการ log ข้อมูลการเข้าระบบของผู้ใช้
* จะเห็นได้จากโค้ดและ Bullets แสดงลำดับขั้นตอนการทำงาน ยิ่งโปรแกรมมีความซับซ้อนเท่าใด การเขียนโค้ดแบบ callback ก็ยิ่งเพิ่มความซับซ้อนให้เรามากขึ้นเท่านั้น
ตัวอย่าง callback hell
- สร้างไฟล์ชื่อ ch7_callback.js
- ป้อนโค้ด ดังนี้
function a(n,cb){
cb(n);
}
a(1,function(n1){
a(2,function(n2){
a(3,function(n3){
a(4,function(n4){
a(5,function(n5){
a(6,function(n6){
a(7,function(n7){
n1+=n2+n3+n4+n5+n6+n7;
console.log(n1);
})
})
});
});
});
})
});
3. รัน node ch7_callback.js
node ch7_callback.js
28
จากตัวอย่างจะเห็นได้ว่าถ้ามีการเขียนโค้ดแบบ asynchronous แล้วต้องรอผลลัพธ์จากอีกฟังก์ชั่นหนึ่ง แล้วมีลำดับต้องทำงานต่อไปเรื่อยๆ โค้ดจะดูน่าเกลียดมาก ซึ่งในตัวอย่างนี้ยังไม่มีการเขียนติดต่อกับไฟล์หรือฐานข้อมูลทำให้อาจจะยังให้มือใหม่ยังไม่เข้าใจเกี่ยวกับ callback และ Asynchronous ได้ 100 % ไว้ในตอนต่อๆไปจะมีการเขียน callback และนำ ไลบรารี่ต่างๆที่เข้ามาช่วยให้บริการจัดการโค้ดได้ดีขึ้น ฝึกบ่อยๆแล้วจะเข้าใจขึ้นเรื่อยๆ
ติดตามกับตอนที่ 8 การเขียน Asynchronous ด้วย Promise