Bluebird NPM: Bluebird JS Promise พร้อมตัวอย่าง
⚡ สรุปอย่างชาญฉลาด
Bluebird JS เป็นไลบรารี Promise ที่มีคุณสมบัติครบครันสำหรับ Javaสคริปต์ที่มีความสามารถหลักคือการแปลงโมดูล Node ที่ใช้ callback ให้เป็นโมดูลที่ส่งคืน promise ดังนั้นจึงสามารถใช้ไลบรารีทั้งหมดเช่นนี้ได้ MongoDB ไดรเวอร์สามารถใช้งานแบบอะซิงโครนัสได้

บลูเบิร์ด JS คืออะไร?
Bluebird JS เป็นไลบรารี Promise ที่มีคุณสมบัติครบครันสำหรับ JavaScript มีคุณสมบัติเด่นที่สุดคือ ช่วยให้คุณสามารถ "แปลง" โมดูล Node อื่นๆ ให้เป็น Promise เพื่อใช้งานแบบอะซิงโครนัสได้ Promiseify เป็นแนวคิดที่นำมาใช้กับฟังก์ชัน callback และรับประกันว่าทุกฟังก์ชัน callback ที่ถูกเรียกใช้จะส่งคืนค่า
ดังนั้นถ้าก Node.js โมดูลนี้มีฟังก์ชันเรียกกลับ (callback function) ที่ไม่ส่งค่ากลับ ซึ่งการใช้ Promise จะทำให้โมดูลนี้แก้ไขฟังก์ชันทั้งหมดในโมดูลโดยอัตโนมัติ เพื่อให้แต่ละฟังก์ชันส่งค่ากลับ
นั่นหมายความว่าคุณสามารถใช้ Bluebird ในการสร้างได้ MongoDB โมดูลทำงานแบบอะซิงโครนัส ซึ่งช่วยเพิ่มความสะดวกสบายอีกระดับเมื่อเขียนแอปพลิเคชัน Node.js
ตัวอย่างด้านล่างนี้ เริ่มต้นด้วยการสร้างการเชื่อมต่อกับคอลเลกชัน “Employee” ในฐานข้อมูล “EmployeeDB” เมื่อสร้างการเชื่อมต่อแล้ว ระบบจะดึงข้อมูลทั้งหมดในคอลเลกชันและแสดงผลในคอนโซล
วิธีสร้างสัญญาด้วย Bluebird JS Library
ต่อไปนี้เป็นตัวอย่างทีละขั้นตอนที่แสดงวิธีการสร้าง Promise โดยใช้ไลบรารี Bluebird JS
ขั้นตอนที่ 1) การติดตั้งโมดูล NPM
ในการใช้งาน Bluebird ภายในแอปพลิเคชัน Node.js จำเป็นต้องติดตั้งโมดูล Bluebird โดยใช้คำสั่งด้านล่าง:
npm install bluebird
ขั้นตอนที่ 2) รวมโมดูล Bluebird เข้าไปด้วย
ขั้นตอนต่อไปคือการรวมโมดูล Bluebird เข้ากับโค้ดของคุณและแปลงโค้ดทั้งหมดให้เป็น Promise MongoDB โมดูล การใช้ Promise ในที่นี้หมายความว่า Bluebird จะรับประกันว่าทุกเมธอดที่กำหนดไว้ในโมดูลนั้นเป็นไปตามเงื่อนไขที่กำหนด MongoDB ห้องสมุดคืนคำสัญญา
Code คำอธิบาย:
- การขอ
requireคำสั่งนี้รวมถึงไลบรารี Bluebird ด้วย - บลูเบิร์ด
.promisifyAll()เมธอดนี้จะสร้างเวอร์ชันแบบอะซิงโครนัสของทุกเมธอด MongoDB โมดูลแต่ละตัวจะทำงานในพื้นหลังและส่งคืนค่า Promise
ขั้นตอนที่ 3) เชื่อมต่อกับฐานข้อมูล
ขั้นตอนสุดท้ายคือการเชื่อมต่อกับฐานข้อมูล ดึงข้อมูลทั้งหมดในชุดข้อมูล และแสดงผลในคอนโซลล็อก
Code คำอธิบาย:
- สังเกตเห็น
connectAsyncใช้วิธีนี้แทนวิธีปกติconnectวิธีการ Bluebird เพิ่มต่อท้าย อะซิงโครนัส คำต่อท้ายสำหรับแต่ละวิธีใน MongoDB ไลบรารีนี้ใช้แยกแยะการเรียกใช้ฟังก์ชันที่ส่งคืน Promise ออกจากการเรียกใช้ฟังก์ชันที่ไม่ส่งคืน Promise เมธอดที่ไม่มีคำต่อท้าย Async จะไม่รับประกันเช่นนั้น - คล้ายกับ
connectAsyncที่findAsyncเมธอดนี้จะส่งคืนเรคอร์ดทั้งหมดใน MongoDB คอลเลกชัน “พนักงาน” - If
findAsyncเมื่อแก้ไขปัญหาสำเร็จแล้ว บล็อกต่อไปนี้จะวนลูปผ่านแต่ละเรคอร์ดในคอลเลกชันและแสดงผลในคอนโซลล็อก
หากดำเนินการตามขั้นตอนข้างต้นอย่างถูกต้อง เอกสารทั้งหมดในคอลเลกชันพนักงานจะปรากฏในคอนโซล ดังที่แสดงในผลลัพธ์ด้านล่าง
นี่คือโค้ดฉบับสมบูรณ์สำหรับการอ้างอิง:
var Promise = require('bluebird'); // promisifyAll adds an Async variant of every MongoDB method var mongoClient = Promise.promisifyAll(require('mongodb')).MongoClient; mongoClient.connectAsync('mongodb://localhost/EmployeeDB') .then(function(db) { return db.collection('Employee').findAsync({}); }) .then(function(cursor) { cursor.each(function(err, doc) { console.log(doc); }); });
⚠️ หมายเหตุเกี่ยวกับเวอร์ชันไดรเวอร์: โค้ดด้านบนมีเป้าหมายที่ MongoDB ไดรเวอร์ Node เวอร์ชัน 2.x ซึ่ง connect แก้ไขด้วยอ็อบเจ็กต์ฐานข้อมูล จากไดรเวอร์ 3.0 เป็นต้นไป มันแก้ไขด้วย MongoClientดังนั้นห่วงโซ่จึงต้องเรียก client.db('EmployeeDB').collection('Employee') แทน db.collection('Employee')ไดรเวอร์เวอร์ชันใหม่ๆ ยังส่งคืนค่า Promise ดั้งเดิมอยู่แล้ว ซึ่งทำให้ promisifyAll ไม่จำเป็นสำหรับ MongoDB เฉพาะ
Bluebird กับ Native Promises: ควรใช้ตัวไหนดีกว่ากัน
เมื่อ Bluebird ถูกเขียนขึ้นครั้งแรก Node.js ยังไม่มีการรองรับ Promise เลย ดังนั้นไลบรารีนี้จึงเข้ามาเติมเต็มช่องว่างที่แท้จริงในแพลตฟอร์ม Node.js ได้เพิ่มการรองรับ Promise แบบเนทีฟตั้งแต่เวอร์ชัน 4 แล้ว async/await ตั้งแต่เวอร์ชัน 7.6 เป็นต้นมา ซึ่งได้เปลี่ยนแปลงวิธีการคำนวณอย่างมากสำหรับทุกคนที่เริ่มต้นโครงการในปัจจุบัน
| แง่มุม | นกชนิดหนึ่ง | คำสัญญาของชนพื้นเมือง |
|---|---|---|
| การอยู่ที่ | แพ็กเกจ npm สำหรับติดตั้งและบำรุงรักษา | สร้างขึ้นในระหว่างการทำงาน |
| การเรียกกลับที่มีแนวโน้มที่ดี | promisifyAll() แปลงโมดูลทั้งหมด |
util.promisify() แปลงฟังก์ชันหนึ่ง |
| ผู้ช่วยพิเศษ | แผนที่, ตัวกรอง, คุณสมบัติ, แต่ละรายการ, หมดเวลา, การยกเลิก | ทั้งหมด, ตกลงกัน, ใดๆ, เฉพาะเชื้อชาติ |
| กองยาว traces | ใช่ เมื่อเปิดใช้งานแล้ว | ปรับปรุงแล้วแต่สั้นลง |
| เหมาะสำหรับ | ไลบรารีเรียกกลับแบบดั้งเดิม ตัวช่วยในการทำงานพร้อมกัน | โค้ดใหม่ และทุกอย่างที่ใช้ async await |
เลือกใช้ native promises สำหรับงานใหม่ เนื่องจากไม่จำเป็นต้องพึ่งพาไลบรารีใดๆ และสามารถทำงานร่วมกับ async await ได้โดยตรง ส่วน Bluebird นั้น หากไลบรารีนั้นยังคงแสดงเฉพาะ callback หรือเมื่อใช้ตัวช่วยในการจัดการการทำงานพร้อมกัน เช่น... Promise.map ด้วยการจำกัดการทำงานพร้อมกัน จะช่วยลดปริมาณโค้ดได้อย่างมาก
วิธีจัดการข้อผิดพลาดด้วย Bluebird Promises
ตัวอย่างข้างต้นไม่มีเส้นทางที่จะเกิดข้อผิดพลาด หาก MongoDB หากกระบวนการไม่ทำงาน หรือชื่อคอลเลกชันไม่ถูกต้อง เชนจะปฏิเสธและไม่มีอะไรแสดงออกมา — กระบวนการจะหยุดทำงานโดยไม่มีคำอธิบายใดๆ เชนของ Promise ทุกตัวจำเป็นต้องมีตัวจัดการการสิ้นสุด (terminal handler)
บลูเบิร์ดสนับสนุน .catch() สำหรับการปฏิเสธและ .finally() สำหรับกระบวนการล้างข้อมูลที่ต้องดำเนินการไม่ว่าในกรณีใดก็ตาม นอกจากนี้ยังรองรับการดักจับแบบระบุประเภท ซึ่งจะจัดการข้อผิดพลาดประเภทหนึ่งในขณะที่ปล่อยให้ข้อผิดพลาดประเภทอื่นแพร่กระจายต่อไป
var Promise = require('bluebird'); var mongoClient = Promise.promisifyAll(require('mongodb')).MongoClient; var connection; mongoClient.connectAsync('mongodb://localhost/EmployeeDB') .then(function(db) { connection = db; return db.collection('Employee').findAsync({}); }) .then(function(cursor) { return cursor.toArrayAsync(); }) .then(function(docs) { docs.forEach(function(doc) { console.log(doc); }); }) .catch(Promise.TimeoutError, function(err) { console.error('The database did not respond in time.'); }) .catch(function(err) { // Any other rejection from any step above arrives here console.error('Query failed:', err.message); }) .finally(function() { // Runs on success and on failure, so the socket always closes if (connection) { connection.close(); } });
มีนิสัยสำคัญสามอย่างที่นี่ อย่างแรกคือ การจัดสถานที่ .catch() ในตอนท้ายแทนที่จะเป็นหลังจากแต่ละขั้นตอน เพราะตัวจัดการปลายทางหนึ่งตัวจะได้รับการปฏิเสธจากทุกลิงก์ก่อนหน้าในห่วงโซ่ ประการที่สอง ให้ใช้ .finally() สำหรับการปล่อยทรัพยากร เช่น การเชื่อมต่อฐานข้อมูล เนื่องจากทำงานทั้งในกรณีที่สำเร็จและล้มเหลว ประการที่สาม อย่าปล่อยให้ห่วงโซ่ไม่มีตัวดักจับ: การปฏิเสธที่ไม่ได้จัดการจะทำให้กระบวนการ Node ยุติลงในเวอร์ชันปัจจุบัน และ Bluebird จะพิมพ์คำเตือนอย่างชัดเจนเกี่ยวกับเรื่องนี้ล่วงหน้า การเปิดใช้งานสแต็กแบบยาว tracระหว่างการพัฒนาด้วย Promise.config({ longStackTraces: true }) ทำให้การระบุที่มาของการปฏิเสธทำได้ง่ายขึ้นมาก
ข้อผิดพลาดและวิธีแก้ไขทั่วไปของ Bluebird Promisify
ปัญหาส่วนใหญ่ของ Bluebird เกิดจากรูปแบบการตั้งชื่อที่ใช้ Promise หรือจากการผสมผสานการเรียก Callback และ Promise ในลำดับเดียวกัน มากกว่าความผิดพลาดใดๆ ในตัวไลบรารีเอง อาการแต่ละอย่างด้านล่างจะระบุสาเหตุและวิธีแก้ไข
- “connectAsync ไม่ใช่ฟังก์ชัน”: จำเป็นต้องใช้โมดูลโดยตรงแทนที่จะผ่านทาง
promisifyAll()ดังนั้นจึงไม่มีเวอร์ชันแบบอะซิงโครนัส ให้ห่อการเรียกใช้ require ตามที่แสดงด้านบน - เรียกใช้เมธอดแบบธรรมดาแล้วได้ค่า undefined:
connect()ยังคงคาดหวังการเรียกกลับ (callback) อยู่ เฉพาะเวอร์ชันที่มีคำต่อท้ายว่า Async เท่านั้นที่จะส่งคืน Promise - การส่งฟังก์ชัน callback ไปยังเมธอดแบบอะซิงโครนัส: อย่าส่งการโทรกลับหรือ
awaitและคำสัญญาจะได้รับการแก้ไขโดยไม่มีใครสังเกตเห็น ควรใช้รูปแบบเดียวต่อการเรียกใช้งาน ไม่ควรใช้ทั้งสองรูปแบบ - ใช้คำสั่ง promisifyAll กับไลบรารีที่ใช้ Promise อยู่แล้ว: ไม่เป็นอันตรายแต่ไร้ประโยชน์ และทำให้เกิดชื่อที่มีคำต่อท้ายซ้ำซ้อนซึ่งทำให้สับสน ตรวจสอบเอกสารประกอบของไลบรารีก่อน
- แก้ไขข้อผิดพลาดโดยไม่แจ้งให้ทราบ: โซ่ที่ไม่มี
.catch()ซ่อนเหตุผลการปฏิเสธจนกว่า Node จะรายงานการปฏิเสธที่ไม่ได้จัดการและออกจากโปรแกรม ควรปิดการทำงานด้วย catch เสมอ แม้แต่ในสคริปต์สั้นๆ ก็ตาม



