ใช้ Node.js วนลูป Callback แบบเรียงลำดับด้วย “async” Library by

24
Apr
0

ในบทความนี้จะพูดถึงการใช้งาน Node.js ในการวนลูป Callback ของการทำงานกับ Array ตามลำดับแบบ Synchronous ซี่งต่อจากนี้ไปจะขออธิบายปัญหาต่างๆ เป็นสถานการณ์ตัวอย่างครับ

สมมุติมี Array ชื่อ activities เก็บข้อมูล Object ดังนี้

var activities = [
{ title: "Raising my hand", time: 1000 },
{ title: "Crying in the rain", time: 5000 },
{ title: "Spinning my head", time: 2000 },
];

สิ่งที่จะทำคือต้องการให้ทำกิจกรรมต่างๆ ตาม [title] ในช่วงเวลา [time] milliseconds ให้ครบทั้ง Array ซึ่งคุณสามารถเขียน Loop เพื่อวนทำกิจกรรมง่ายๆ ได้ดังนี้

for(var i = 0; i < activities.length; i++) {
(function(){
var title = activities[i].title;
var time = activities[i].time;
console.log("+ " + title + " for " + time/1000 + " second(s)");
setTimeout(function(){
console.log("- " + title + " completed!");
}, time);
})();
}

จากโค้ดก็ไม่มีอะไรมาก เป็นเพียงการวนลูป setTimeOut ทำกิจกรรมตามเวลาของแต่ละกิจกรรม

ซึ่งเมื่อรันโค้ดดังกล่าว ผลลัพธ์ที่ได้คือ
+ Raising hand for 1 second(s)
+ Crying in the rain for 5 second(s)
+ Spinning head for 2 second(s)
- Raising my hand completed!
- Spinning my head completed!
- Crying in the rain completed!

สังเกตว่า การทำ Raising hand, Crying in the rain, Spinning head นั้นจะเริ่มทำพร้อมกันไปเลย เมื่อตัวไหนทำเสร็จก่อนก็จะเข้าสู่ Callback Function และรายงานผลว่าเสร็จแล้ว ซึ่งการทำแบบนี้เป็นวิธีการแบบ Asynchronous ตามแบบฉบับของ Node.js ที่ใช้ Javascript เป็นฐานอยู่แล้ว

แต่ในบางครั้ง เราต้องการผลลัพธ์ที่มีการกระทำตามลำดับทีละรายการ เช่น จากตัวอย่างด้านบนเราต้องการ Raising hand 1 วินาทีให้จบก่อน จากนั้นค่อย Crying in the rain 5 วินาที หลังจากนั้นค่อย Spinning Head อีก 2 วินาทีตามลำดับ จึงต้องมีการจัดลำดับ ให้ทำงานเป็น Synchronous ให้ได้ (ร้องไห้ไป หมุนหัวไป ยกมือไปพร้อมกันคงพิลึก)

ซึ่งถ้าเขียนตามปกติก็จะต้องเขียนโค้ดให้มีหลักการคร่าวๆ ประมาณดังนี้

console.log("+ Raising my hand for 1 second(s)");
setTimeout(function(){
console.log("- Raising my hand completed!");
console.log("+ Crying in the rain for 5 second(s)");
setTimeout(function(){
console.log("- Crying in the rain completed!");
console.log("+ Spinning my head for 2 second(s)");
setTimeout(function(){
console.log("- Spinning my head completed!");
}, 2000);
}, 5000);
}, 1000);

ผลลัพธ์ที่ได้

+ Raising my hand for 1 second(s)
- Raising my hand completed!
+ Crying in the rain for 5 second(s)
- Crying in the rain completed!
+ Spinning my head for 2 second(s)
- Spinning my head completed!

จะเห็นว่าผลลัพธ์ถูกต้องทุกประการ ทำตามลำดับอย่างดี แต่น่าเสียดายที่ตัวอย่างข้างต้นเป็นเพียงหลักการที่มีจำนวนชั้นของ Callback คงที่ ไม่สามารถนำไปใช้จริงกับ Array “ativities” ในตัวอย่างได้ เพราะเป็นไปไม่ได้เลยที่เราจะวนลูปเรียก Callback ในแต่ละสมาชิกของ Array ไปเรื่อยๆ จนกว่าจะครบทุกสมาชิกใน Array

วิธีการแก้ปัญหาอาจมีอยู่มากมาย แต่ในบทความนี้ขอนำเสนอคำสั่ง async.eachSeries() ซึ่งเป็นคำสั่งหนึ่งที่อยู่ใน Library ยอดนิยม “async” และมีรูปแบบการใช้งานที่ไม่ยุ่งยากมากนัก วิธีการใช้งาน ในขั้นตอนแรกจำเป็นต้องมี Library “async” เสียก่อน สามารถติดตั้งได้โดยพิมพ์คำสั่งด้านล่างใน Directory ที่ต้องการบนหน้าจอ Terminal หรือ Command Line

npm install async

และมีการใช้งานดังนี้

async.eachSeries(arr, iterator, callback);

โดย

  • array: Array ที่ต้องการท่องเข้าไป
  • iterator: ฟังก์ชันการทำงานที่ต้องการ
  • callback: สิ่งที่จะทำหลังจากการท่องไปยัง Array เสร็จสิ้นแล้ว

จากปัญหาที่กล่าวมา สามารถเขียนโค้ดได้ดังนี้

async.eachSeries(activities, function(activity, next)
{
var title = activity.title;
var time = activity.time;

console.log("+ " + title + " for " + time/1000 + " second(s)!");
setTimeout(function(){
console.log("- " + title + " completed!");
next();
}, time);
}, function(){
console.log("= All completed!");
});

จากตัวอย่างจะมีโค้ดที่คล้ายคลึงกับตัวอย่างแรกสุด แต่สิ่งที่ต้องมีในของ iterator มีดังนี้

  • พารามิเตอร์แรก (activity) เป็นพารามิเตอร์ที่เป็นตัวแทนของสมาชิกแต่ละตัวของ Array (ในที่นี้หมายถึงสมาชิกแต่ละตัวของ Array activitities)
  • พารามิเตอร์ที่สอง (next) เป็นพารามิเตอร์ที่เป็นฟังก์ชัน เพื่อนำไปเรียกใช้อ้างอิง Callback ของสมาชิกใน Array ตัวถัดไป
  • การเรียกใช้พารามิเตอร์ที่สอง (การใช้ next()) เป็นการบ่งบอกว่าจะไปเรียก Callback ของสมาชิกใน Array ตัวถัดไปแล้ว

เมื่อลองรันดู ผลลัพธ์ที่ได้คือ
+ Raising my hand for 1 second(s)!
- Raising my hand completed!
+ Crying in the rain for 5 second(s)!
- Crying in the rain completed!
+ Spinning my head for 2 second(s)!
- Spinning my head completed!
= All completed!

ซึ่งผลลัพธ์สามารถใช้งานร่วมกับ Array และตอบปัญหาของบทความนี้ได้แล้ว นั่นคือ Raising hand 1 วินาทีให้จบก่อน จากนั้นค่อย Crying in the rain 5 วินาที หลังจากนั้นค่อย Spinning Head อีก 2 วินาทีตามลำดับ

สามารถเข้าไปดูรายละเอียดการใช้งานและลูกเล่นอื่นๆ เพิ่มเติมได้ที่ https://github.com/caolan/async


กู้เงิน | เศรษฐกิจพอเพียง | สินเชื่อบุคคล | สมัครบัตรกดเงินสด | สินเชื่อ | เงินกู้ด่วน | ยืมเงินทรูมูฟ | เงินด่วนนอกระบบ