ทำไมต้อง Node.JS? by

28
Feb
0

ภาษาฝั่ง server ยอดนิยมทุกวันนี้คงหนีไม่พ้น php, python, ruby แต่เหตุใด Node.JS ถึงมาแรงขึ้นเรื่อยๆ และเป็นภาษาที่น่าจับตามองในปีนี้? เหตุผลหลักอยู่ที่นี่แล้ว!

  • เป็นภาษาที่ออกแบบมาเป็น Event-Driven คือทำงานตามเหตุการที่เกิด ซึ่งเรียกอีกอย่างได้ว่ามันทำงานแบบ Asynchronous
  • จริงๆ ภาษาอื่นก็มี Framework ที่ช่วยให้เขียนแบบ Event-Driven เหมือนกันเช่น Photon(PHP), Tornado (Python) แต่สิ่งที่ Node.JS เด่นกว่าชาวบ้านคือมันเกิดมาเพื่อเป็น Asynchronous มาแต่แรก ทำให้ library ต่างๆ กว่า 90% ทำงานแบบ Asynchronous ทั้งหมด หากเป็นภาษาอื่นจะหา library ที่เป็นแบบ Asynchronous ยากกว่ามากเพราะโดยพื้นฐานออกแบบมาให้ทำงานแบบ Synchronous ซึ่งหากเผลอรัน code ที่ทำงานแบบ Synchronous ในระหว่างทำงาน Asynchronous code จะพังได้ ซึ่งคนที่ไม่คุ้นจะเผลอเรียกคำสั่งที่คุ้นเคยได้ง่ายมาก สรุปคือ library ที่เคยใช้เช่น mysql แบบเดิมๆ ก็จะใช้ไม่ได้ ต้องรื้อ code ใหม่หมด ปรับความเคยชินใหม่หมดอยู่ดี มีแค่ syntax ที่ยังเหมือนเดิม แต่การทำงานต่างจากเดิมโดยสิ้นเชิง
  • เนื่องจากการทำงานฝั่ง web server มีการ “รอ” กันมากมาย ดังนั้นเมื่อใช้ Node.JS จะช่วยลดการรอลงได้มาก ตัวอย่างการต่อเช่น รอ mysql ทำงานเสร็จ, รอการเขียนลงไฟล์, รอรับ request มาจากผู้ใช้หลายๆ คน ทุกอย่างสามารถทำไปพร้อมๆ กันได้ทั้งหมด
  • Node.JS เป็นทั้ง Web Server (แทน Apache) เป็นได้ทั้ง Load Balancer (แทน Nginx, Haproxy) และเป็นตัว Application เอง ทำให้สามารถทำงานเสร็จทั้งหมดในตัวมันเอง
  • Node.JS ทำงานแบบ Single thread (เพราะต้องทำหน้าที่ web server) และขณะทำงานจะไม่มีการปิด process ตัวเองทิ้ง process จะรันตลอดเวลาเพื่อรอรับ request จากผู้ใช้เข้ามาอยู่ตลอด หรือพูดง่ายๆ คือ “process เดียวทำทุกอย่าง” (ยกเว้นเขียน cluster ซึ่งถ้าโหลดไม่มากจริงๆ ก็ไม่ต้องใช้) ทำให้เหมาะแก่การทำเป็น background process คอยทำงานบางอย่างตลอดเวลามากกว่า php ที่ process เกิดแล้วตายเรื่อยๆ ตลอด
  • เนื่องจากมันมี process เดียว ทำให้เราสามารถตั้งตัวแปร global ขึ้นมาตัวนึงแล้วสามารถใช้ร่วมกันระหว่าง user ทุกคนได้โดยไม่ต้องสร้างใหม่ตลอดเวลา เช่นการทำ cache อะไรบางอย่างที่ปกติต้องเก็บใน memcache พอจะใช้งานก็ดึงออกมา unserialize ทุกๆ ครั้ง ทำให้เสีย cpu ไปไม่ใช่น้อย ถ้าใช้ node.js คุณอาจไม่ต้องใช้ memcache เลย เมื่อดึงข้อมูลจาก database มาครั้งแรกครั้งเดียวก็สามารถเก็บลงตัวแปรไว้ reuse ได้ตลอดเวลา ไม่ต้องไปพักค่าที่ memcache และดึงมา unserialize ทุกครั้งอีกต่อไป
  • จากข้อที่แล้ว เลยทำให้มีทรัพยากรที่ใช้ร่วมกัน ลด Memory ที่ต้องใช้ลงได้มาก

ตัวอย่าง pseudo code แบบ synchronous:


a = mysql_query("SELECT * FROM user WHERE id = 1");
b = mysql_query("SELECT * FROM item");
show_user(a);
show_item(b);

จาก code ด้านบน สมมติบรรทัดแรกใช้เวลาทำงาน 10 วิถึงจะได้ค่าลงตัวแปร a และอีก 3 วิจะได้ค่าลงตัวแปร b รวมเป็น 13 วิ จึงจะแสดงผล item ได้ทั้งๆ ที่ item ใช้เวลาดึง query มาน้อยกว่า

ตัวอย่าง pseudo code แบบ asynchronous:

mysql_query("SELECT * FROM user WHERE id = 1", function(error, result){
show_user(a);
});
mysql_query("SELECT * FROM item", function(error, result){
show_item(result);
});

จากตัวอย่างข้างบนแม้การดึงข้อมูล user จะกินไป 10 วิ แต่เนื่องจากไม่ต้องรอให้ดึง user เสร็จก่อนก็สามารถดึงข้อมูล item ต่อได้เลย ทำให้สามารถ show_item ได้โดยใช้เวลา 3 วิ และ user ก็จะแสดงผลทีหลัง ทำให้การทำงานโดยรวมเร็วขึ้น วันนี้เอาเท่านี้ก่อนละกันครับ :)

มาใช้ EventEmitter กันดีกว่า by

31
Jan
1

EventEmitter คือ class ที่เอาไว้สร้างเหตุการณ์ต่างๆ สำหรับ Node.JS ถ้าเปรียบเทียบกับงานที่ทุกคนคงรู้จักกันก็เช่น event onclick เมื่อมีการคลิกให้ทำการเรียก function ที่กำหนดให้อัตโนมัติ ในบางภาษาจะเรียก AddEventListener นั่นเอง บางคนอาจจะคิดว่าทำต้องทำต้องเป็น event ด้วยในเมื่อเรียก function ตรงๆ ก็ได้ คำตอบคือการทำงานของ class EventEmitter จะเป็นการประกาศดักรอ event เมื่อ event เกิดขึ้นจึงค่อยทำงาน ซึ่งรูปแบบการทำงานจะเป็นลักษณะ passive รอถูกเรียก และสามารถประกาศรอรับ event กี่ที่ก็ได้ ทุกๆ ที่จะโดนเรียกทั้งหมด ทำให้สามารถทำงานได้สะดวกขึ้น

EventEmitter จะมีสอง method หลักๆ ที่ใช้งานคือ on และ emit ซึ่ง on เอาไว้ดักรอ event แล้วค่อยเรียก callback function ให้ทำงาน (AddEventListener)  ส่วน emit เอาไว้เป็นตัวสั่งการให้เกิด event ชื่อที่กำหนด และจะไปเรียก callback function ที่ประกาศรอไว้ในตอนแรกจนครบโดยอัตโนมัติ (ใน flash emit ก็คือ dispatchEvent นั่นเอง)

ตัวอย่าง code:
var EventEmitter = require('events').EventEmitter;
var util = require('util');
// create the class
var MyClass = function () { }
// inherit class
MyClass.prototype.__proto__ = EventEmitter.prototype;

var obj = new MyClass();
obj.on('startProgram', function(arg){
console.log('startProgram event run '+arg);
});

obj.on('startProgram', function(arg){
console.log('startProgram event run point 2 '+arg);
});

obj.emit('startProgram', '1');

ผลลัพธ์
startProgram event run 1
startProgram event run point 2 1

จะเห็นได้ว่าประกาศ on ไว้ 2 ที่ ก็จะโดนเรียกทั้ง 2 ที่ ซึ่งเวลาใช้งานจริงสามารถประกาศ on ที่ไฟล์อื่นก็ได้ (แต่ต้องส่ง instance ของ class ไปเพื่อเรียกใช้ด้วย) ทำให้เวลาทำงานจริงเราสามารถกำหนดโครงสร้างต่างๆ ได้ง่าย ตัวอย่างการใช้งาน EventEmitter อีกตัวที่เห็นได้ชัดก็คือ Socket.IO ที่ใช้ติดต่อส่งข้อมูลผ่าน network ไปยัง client นั่นเอง หรือแม้แต่ express ก็ใช้ EventEmitter นี่เช่นกัน จะเห็นได้ว่า EventEmitter เหมาะสำหรับงานทางด้าน network เป็นที่สุด เอาไว้สร้าง code ส่วนที่เป็น asynchronous ได้โดยง่าย

วิธี remote debugging node.js ด้วย node-inspector by

31
May
0

เคยใช้พวก Watch, Breakpoint, Step into, Step Over, Step Out ช่วยในการ debug javascript ใน Chrome ไหมครับ รู้หรือไม่ว่าเราสามารถ remote debug code node.js ไปยังเครื่อง server ที่อยู่คนละที่ได้เลย :) วิธีการมีดังนี้ครับ

  1. npm install -g node-inspector
  2. เพิ่ม debugger; ในบรรทัดที่ต้องการ set breakpoint ภายในโปรแกรม node.js ของเรา
  3. node-inspector –web-port=1338 & (เปลี่ยน 1338 เป็นเลข port ที่คุณต้องการเปิดให้ debug เข้ามาได้ หรือถ้าไม่ใส่จะ default ที่ 8080)
  4. หากโปรแกรมคุณชื่อ server.js สั่งรันดังนี้คือ node –debug node server.js (ห้ามเอา –debug ไปต่อท้าย ต้องอยู่หลัง node เท่านั้น)
  5. เข้าเว็บ http://localhost:1338/debug?port=5858 (แก้ localhost เป็นชื่อเว็บที่คุณใช้งานได้เลย)
  6. จะปรากฎดังรูป พร้อมให้คุณใช้งาน debug!
    node-inspector-google-chrome_2013-05-31_21-19-32
  7. ส่วนที่วงสีแดงไว้ในข้อ 6 คือเราสามารถเปิด browse file ใน project ของเราเพื่อมา set breakpoint เพิ่มสดๆ ด้วยการคลิกที่เลขบรรทัดทางซ้ายได้เลยทันที สะดวกต่อการใส่ breakpoint เฉพาะเวลาที่ต้องการเท่านั้น
  8. สามารถเอาเม้าส์ชี้เพื่อดูค่าตัวแปร, add to watch, step into, step over, step out ได้ครบถ้วน Yay!

ขอให้โชคดีกับ Node.js ทุกๆ ท่าน :D

การทำ callback หลังจบลูป by

30
Apr
0

ในการใช้งานของ node.js นั้น จะมีพื้นฐานการทำงานแบบ Asynchronous ทำให้บางครั้ง

การที่เราต้องการค่าตัวสุดท้ายที่ออกมาจากลูป นั้นทำได้ยากมาก

ตัวอย่างเช่น

เรามี array อยู่ 1 ชุดจากนั้นเราต้องการ เอาค่าต่างๆใน  array ชุดนั้นมาเข้าฟังก์ชั่นที่ต้องรอ callback อีกทีหนึ่ง

แล้วเราก็ต้อง callback ค่าที่ได้นั้นๆออกมาเป็น array อีกที เราจะมีวิธีเขียนเพื่อทำงานในลักษณะนี้ยังไง

วิธีแก้ไขปัญหานี้ เราจะใช้ parallel ของ async lib เข้ามาช่วยโดยโครงสร้างของมันมีดังนี้
parallel(tasks, [callback])
async.parallel([
function(callback){
setTimeout(function(){
callback(null, 'one');
}, 200);
},
function(callback){
setTimeout(function(){
callback(null, 'two');
}, 100);
}
],
// optional callback
function(err, results){
// results จะมีค่าเป็น array [one,two]
});

จากตัวอย่างการเขียนเราสามารถเขียน เป็น Array ของ function ให้เสร็จก่อนแล้วค่อยเรียกใช้งาน parallel ได้ แต่สิ่งที่เราต้องการนั้น
เราต้องเอาค่าใน array ไปเรียกใช้ function ก่อนด้วย จะมีลักษณะ การใช้ดังนี้
var aData = [1,2,3];
var aFunc = [];
for (var i in a)
{
//สั่งให้ มันรันคำสั่งนี้ทันที
(function(){
var temp = a[i];
var func = function(callback){
// เรียกฟังก์ชัน อืนเพื่อประมวนผลก่อน
funcA (temp, function(result){
callback(null,result);
}
};
aFunc.push(func);//เก็บ function ลง array
})();
}
async.parallel(aFunc,function(err,results){
//จะได้เป็น array ของข้อมูลที่ออกมาจากฟังก์ชั้น funcA});

ใช้ 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


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