สอนทำเว็บไซต์ด้วย Node.js, Express และ MongoDB ตอนที่ 3 - พื้นฐาน Node.js
มาต่อกันที่ ตอนที่ 3 กันครับ
เนื้อหาบทเรียน
- ตอนที่ 1 - NodeJS คืออะไร + ทำการติดตั้ง Node.js และ Node.js เบื้องต้น
- ตอนที่ 2 - ทบทวนพื้นฐาน JavaScript และ Modern JavaScript ES6, ES7+
- ตอนที่ 3 - ว่าด้วยพื้นฐาน Node.js / Callback / Sync และ Async(บทความนี้)
- ตอนที่ 4 - เริ่มต้นทำเว็บด้วย Node.js และ Express.js
- ตอนที่ 5 - ลองหัดใช้ Template Engine ชื่อ Pug
- ตอนที่ 6 - เริ่มต้นกับ MongoDB
- ตอนที่ 7 - ทำ Backend API ด้วย Node.js และ MongoDB กันดีกว่า
- ตอนที่ 8 - Express Generator / Middleware
- ตอนที่ 9 - ทำระบบ Login ด้วย Passport.js
- ตอนที่ 10 - การ Hosting และ Deploy Production
Callback
ก่อนจะไปถึง Node.js ขอพูดึงเรื่องของ Callback ก่อนนะครับ เนื่องจากว่าในการเขียน Node.js เราจะเห็นการใช้งาน callback บ่อยมากๆ และจริงๆแล้ว callback ไม่ได้มีแค่ใน Node.js ส่วนที่เป็น client บน Browser ก็จะเห็นเช่นกัน หรือ library ดังๆเมื่อก่อน เช่น jQuery หรือ event listener ใน Browser ก็ล้วนมีการใช้ callback ทั้งสิ้น
ใน Node.js ตัวมันถูกออกแบบมาให้ทำงานแบบ Asynchronus คือไม่จำเป็นต้องทำงานพร้อมกัน หรือรอให้คำสั่งนึงเสร็จก่อน ค่อยทำคำสั่งถัดไป แต่มันสามารถทำพร้อมกัน 2 คำสั่ง และไม่ต้องรอผลลัพธ์ เรียกได้ว่า เหมือนทำใครทำมัน คำสั่งแรก อาจจะได้ผลลัพธ์ทีหลังก็ได้
Callback คือ function ที่เราส่งไปให้กับอีก function (ปกติเราส่งแค่ค่าตัวแปร แต่ใน JavaScript ตัว function เป็น first class citizen สามารถส่ง function ผ่าน function ได้)
ตัวอย่าง Callback ง่ายๆ
- อธิบายคือ ฟังค์ชั่น
myCallback
รับ parameter เป็น function และ ทำการ return function โดยทำการ call function และส่งDevahoy
ไปเป็น argument ของ function - พอ
myCallback(hello)
ถูก call ตัว myCallback ส่งhello
ฟังค์ชั่น เป็น argument พอเข้า function มันก็เลย returnHello Devahoy
(มองว่าcallback('Devahoy')
ก็คือhello('Devahoy')
) ต่างกันแค่ชื่อ function เพราะเราจะตั้งเป็นอะไรก็ได้ เวลาเราตั้ง parameter.
ทีนี้ใน Node.js การใช้ callback จะเป็น style แนวๆ Error Convention คือ callback function ของ Node.js จะต้องเป็น function ที่มี 2 parameter ครับ คือตัวแรก คือ Error และตัวที่สอง คือ data ผลลัพธ์
ตัวอย่างเช่น
Promise
อธิบาย Promise ซักนิด จริงๆแล้ว ไม่ใช่ Node.js แต่มันเป็นส่วนนึงของ JavaScript จริงๆแล้ว Promise ก็คือ Object ที่เอาไว้ทำงานพวก async ครับ ซึ่งเมื่อก่อนอาจจะมีแค่ return callback หลังๆ อาจจะเริ่มมีการใช้ Promise มากขึ้น ตัวอย่างเช่น การใช้ Promise ก็จะมีการ chain method แบบ then()
และ catch()
ครับ ซึ่งส่วนนี้ถ้าใครเคยเห็น หรือเคยเขียน Node.js มาบ้าง อาจจะคุ้นๆ เช่น
ซึ่งเดี๋ยวพอเป็น Promise ในบทความถัดๆไป ผมจะพยายามอธิบายเพิ่ม หรือมีตัวอย่างเพิ่มเติมเรื่อยๆครับ เผื่อใครที่ยังไม่ค่อยเข้าใจ เพราะเวลาเห็นการใช้จริงแล้ว ก็อาจจะเข้าใจมากขึ้น แล้วค่อยย้อนกลับมาลง Detail ครับ
File System
มาลองดูตัวอย่าง Node Modules ของ Node.js ที่ built-in มาตอนที่เราติดตั้งกันครับ จะได้มองเห็นภาพ และตัวอย่างการใช้งาน และเขียน Node.js มากขึ้น
ชื่อ module ว่า fs
นะครับ เป็น การอ่าน เขียน ไฟล์ ด้วย Node.js
วิธีการใช้งาน ให้ทุกคนสร้างไฟล์ขึ้นมาใหม่เลย ตั้งชื่อว่า main.js
จากนั้น import fs
เข้ามาครับ
readFile - อ่านไฟล์
การอ่านไฟล์ ด้วยฟังค์ชั่น readFile(path, callback)
argument ที่เราจะต้องส่งไปคือ ตัวแรกเป็น path ที่อยู่ไฟล์ของเรา และ 2 คือ callback function นั่นเอง แบบนี้
จากนั้นลองสร้างไฟล์ ขึ้นมา data.txt
และใส่ข้อมูลลงไปใน Text จะใส่อะไรก็ได้ครับ เช่น
จากนั้นลองรันคำสั่ง node main.js
และดูผลลัพธ์ครับ เป็น Buffer แบบนี้ครับ
ถ้าเราอยากให้เป็น String เราต้อง pass options ให้มันครับ เป็นแบบนี้
ซึ่งปกติ options จะเป็น Object แต่ถ้าเป็น string ตัว readFile
มันจะเข้าใจว่าคือ encoding นั่นเอง ลองรันใหม่ซิ node main.js
ได้ผลลัพธ์แล้ว
ทีนี้อย่างที่บอก Node.js เป็น async เราลองเรียก readFile
2 ครั้ง แล้วมี console.log แบบนี้ดู
เมื่อเรารัน node main.js
เราจะเห็นผลลัพธ์เป็นแบบนี้
จะเห็นได้ว่า start
แล้ว end
เลย ส่วนค่าที่ readFile
มันถึงแสดงทีหลัง และก็ไม่การันตีว่า data 1
ต้องมาก่อน data 2
ใครเสร็จก่อน ก็ได้ผลลัพธ์ก่อน ลองรันหลายๆรอบดูก็ได้ครับ เพราะเนื่องมาจากการเป็น async callback เลยไม่ต้องรอผลลัพธ์นั่นเอง ซึ่งหลายๆคน ที่มาเขียน Node.js เข้าใจผิดว่า และเขียนคล้ายๆแบบนี้ครับ
มันจะได้เป็น undefined แบบนี้
ซึ่งถ้าหากใครเจอปัญหา undefined เวลาที่รับค่าจาก node.js หรือพวก function ต่างๆ ให้พึงนึกเสมอครับ ว่าเป็น async เราต้องใช้ callback ในการรับค่า หรืออาจจะอยู่ในรูปแบบ Promise ก็ได้ครับ
แต่ถ้าหากใครอยาก readFile แบบให้มันเป็น sync รอผลลัพธ์ก่อน ค่อยทำต่อ ก็ใช้ readFileSync
ได้ครับ แบบนี้
แต่ข้อควรระวัง Node.js จุดขายของมันคือการทำงานแบบ Async ครับ หากเราใช้แต่ sync เพื่อรอผลลัพธ์ คิดดูเล่นๆ หาก 1 request เราต้องทำงานหลายๆ อย่าง และต้องรอคิวแรกเสร็จ คนที่สองค่อยทำงาน มันก็จะช้ามากๆ
writeFile - การเขียนไฟล์
การเขียนไฟล์ใน Node.js ใช้ writeFile(FILE, data, callback)
แบบเดียวกันกับตอนอ่านไฟล์เลยครับ เช่น
เช่นเดียวกับ readFile
ตัว writeFile
ก็มีแบบ synchronous ครับ คือใช้ writeFileSync()
นั่นเอง โดยไม่ต้องส่ง callback และไม่มีผลลัพธ์กลับมานะครับ
ตัวเขียนไฟล์ ทุกครั้งที่สั่ง node main.js
มันจะเขียนไฟล์ทับลงไปในไฟล์เดิม และเซฟทุกครั้ง แต่ถ้าเราอยากเขียนไฟล์ โดยที่ไม่ต้องลบเนื้อหาเก่าละ คือเขียนเพิ่มลงไปในไฟล์ต่อกันเลย
เราจะใช้ appendFile()
ครับ เหมือนกับ writeFile
ทุกอย่างเลย ต่างกันที่ appendFile
มันเขียนข้อมูลต่อกัน ขณะที่ writeFile
จะเขียนใหม่ทุกครั้ง
Module scope
นอกเหนือจาก Module แล้ว มีอีก 2 อันที่อยากจะแนะนำครับ คือ
__dirname
เป็นตัวแปรเพื่อดึงค่า ตำแหน่ง folder หรือ directory ของเราครับ__filename
จะได้ชื่อไฟล์ที่โปรแกรมมันทำงานอยู่
เช่นลองเพิ่มลงไปที่ main.js
ดูครับ
Path
ขอแนะนำอีก Module เอาไว้แก้ปัญหาครับ จะเห็นว่าตัวอย่างด้านบน การอ่านไฟล์ การเขียนไฟล์ จะไม่มีปัญหาเลย ถ้าเรารันคำสั่ง ในโฟลเดอร์เดียวกัน เช่น เราอยู่โฟลเดอร์ /my-app/node-tutorial
และรัน node main.js
ที่โฟลเดอร์นั้น มันก็จะอ่านไฟล์จากโฟลเดอร์ถูก แต่ถ้าเรา อยู่ที่โฟลเดอร์ my-app
แล้วรัน node node-tutorial/main.js
มันก็จะอ่าน และเขียนไฟล์ จากโฟลเดอร์ my-app
ไม่ใช่ my-app/node-tutorial
แล้ว
วิธีแก้ไขปัญหานี้คือใช้ __dirname
เพื่อบอกให้ Node รู้ว่า __dirname
ของไฟล์ มันคือ path ไหน เพราะเรารัน node จากที่ไหนก็ได้ __dirname
ค่าเดิม
สิ่งที่ path มีคือ path.join()
ครับ คือเหมือน path มาต่อกันนั้นเอง เช่น
เราก็เลยใช้ร่วมกับ __dirname
เป็นแบบนี้
สุดท้าย ลองแก้ appendFile
ด้านบน โดยใช้ path ด้วย จะได้เป็นแบบนี้ครับ
HTTP
ต่อมาสุดท้ายของบทความนี้ละครับ คือ Module HTTP ที่ทำให้เราสามารถนำ Node.js มาเป็น Web Server ได้นั้นเอง ข้อดีของ HTTP คือทำตัวเป็น server พร้อมรับ request และส่ง response กลับไปที่ client ได้ครับ
ลองสร้างไฟล์ขึ้นมาใหม่ ชื่อ app.js
เมื่อเรามีโค๊ด ด้านบน ลองสั่ง node app.js
แล้วเปิดหน้าเว็บ http://127.0.0.1:3000/ เราก็จะเห็นคำว่า Hello World นั่นเอง ที่เป็น String จริงๆ เพราะถูก set content-type เป็น text/plain
ทีนี้ลองแก้นิดหน่อย เปลี่ยนตรง createServer
เป็นแบบนี้
เนื่องจากผมย้าย listener function ไปเป็น function ชื่อ handler เพื่อให้ดูง่ายๆ แล้วเวลา createServer()
ค่อยส่ง function ไป จะได้ไม่ต้องเขียน function ซ้อนๆกันครับ มือใหม่หลายคนอาจจะงงได้
และตอนนี้ผมเอา setHeader
ออกแล้ว เนื่องจากไม่ต้องให้เป็น text/plain
เพราะไม่งั้นมันจะมอง html tag เป็น string (หรือจริงๆ เป็น content type text/html
ก็ได้ครับ)
จะได้ผลลัพธ์เป็นแบบนี้ครับ เพิ่มสี และปรับ style ได้เหมือน html ปกติเลย (และเนื่องจากผมใช้ ES6 String literal ทำให้ผมสามารถทำ string หลายบรรทัดโดยใช้ backtick ``` ได้นั่นเอง อ่านง่ายกว่า string ยาวๆ)
ตอนนี้เราก็สามารถสร้าง Server ด้วย Node.js ง่ายๆ ได้แล้ว
ทีนี้ลองอีกแบบ เราจะส่ง string กลับไป แต่เราจะไม่มานั่งพิมพ์ในไฟล์เดียวกัน แต่เราจะอ่านจาไฟล์แทน ลองสร้างไฟล์ index.html
ขึ้นมาไฟล์นึง
มีข้อมูลแบบนี้
จากนั้น เราก็ใช้ fs.readFile
ครับ เพื่ออ่านข้อมูลจาไฟล์ index.html
แล้วส่งกลับไป พวก response
ด้วย res.end()
เลยแบบนี้
หรืออีกวิธีคือ การอ่านไฟล์ด้วย fs.createReadStream
ครับ จะอ่าน text ได้ไวกว่า readFile
ซึ่งถ้าข้อมูลมากๆ เราจะเห็นผลชัดเจนครับ
ตอนนี้เราก็สามารถทำเว็บด้วย Node.js แบบง่ายๆได้แล้วเนาะ ตอนนี้ก็พอมองเห็นภาพได้มากขึ้นแล้วใช่มั้ยครับ ในบทความถัดๆไป จะเริ่มไปส่วน Express ที่เป็น Web Framework มาช่วยให้เราทำเว็บได้ไวขึ้น และสะดวกง่ายขึ้นมากๆ
ซึ่งจริงๆแล้ว Node.js ยังมี built-in อื่นๆอีกเพียบเลยนะครับ แต่ตัวอย่างหรือบทความนี้ขอยกตัวอย่างคร่าวๆ ละกันเนาะ เพราะมันค่อนข้างใช้งานบ่อยเลย และ module อื่นๆ จริงๆแล้ว มันก็ไม่ยากเลยครับ อ่าน Document และลักษณะการใช้ก็เหมือนกับ fs
เลย
สุดท้ายใครมีปัญหา ติดปัญหาตรงไหน สามารถสอบถามเพิ่มเติมได้ตลอดครับ แล้วพบกันบทความถัดไป ติดตามอ่านได้เลยครับ
อ่านต่อ ตอนที่ 4 - เริ่มต้นทำเว็บด้วย Node.js และ Express.js
ส่วน Source Code (อยู่ part3) สามารถเข้าไปดาวน์โหลด หรือ clone ผ่าน Github ได้เลย หากใครไม่รู้จัก Git สามารถอ่านบทความนี้เพิ่มได้ครับ Git คืออะไร ? + พร้อมสอนใช้งาน Git และ Github
❤️ Happy Coding
- Authors
-
Chai Phonbopit
เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust