Node.js 8: แปลง callback เป็น Promise ด้วย util.promisify
ปัญหาหนึ่งของการใช้งาน JavaScript นั่นก็คือ Callback Hell ที่จะทำให้คุณเร่าร้อนดั่งไฟนรกทุกครั้งที่ต้องอ่านโค้ด โดยเฉพาะอย่างยิ่งกับ callback สไตล์ฉบับนู๋ Node.js นึกภาพไม่ออก? ลองดูโค้ดข้างล่างดูฮะ
1const fs = require('fs')2fs.readFile('./a.js', 'utf8', (err, text) => {3 if(err) {4 console.log('Error', err)5 } else {6 // ทำอะไรซักอย่างกับข้อมูลที่ได้จากไฟล์ a แล้วค่อยอ่านไฟล์ b ต่อ7 fs.readFile('./b.js', 'utf8', (err, text) => {8 if(err) onsole.log('Error', err)9 else { //... }10 })11 }12})
ใน fs.readFile
เรามีการส่ง callback เข้าไปเป็นอาร์กิวเมนต์ตัวสุดท้าย ในตัวอย่างนี้เมื่อไฟล์ a.js
ถูกโหลดเสร็จสิ้นจึงทำการโหลด b.js
ในลำดับถัดมา ด้วย callback ที่ซ้อนกันลึกแบบนี้จินตนาการซิถ้ามีซัก 3 - 5 callbacks จะเหมือนตกนรกซักแค่ไหน!
เมื่อ Callback Hell ครองเมือง ในฐานะโปรแกรมเมอร์เราจึงต้องมีที่ยืน และนั่นจึงเป็นเหตุให้ Promise และ Async / Await เกิดขึ้นเพื่อแก้ปัญหาของ callback ยังงงอยู่? เสพบทความ กำจัด Callback Hell ด้วย Promise และ Async/Await ก่อนซิ!
ช่างน่าเสียดายที่ callback ลูกทุ่งสไตล์ Nodeๆ ไม่สามารถใช้งานกับ Promise ได้ promise library ต่างๆเช่น Bluebird เลยต้องมีวิธีการแปลง callback สไตล์โหนดๆ ให้ใช้คู่กับ promise ได้ เช่น
1const fs = require('fs')2const Promise = require('bluebird')3const readFile = Promise.promisify(fs.readFile)45readFile('./a.js', 'utf8')6 .then(text => readFile('./b.js', 'utf8')7 .then(text => //..)8 .catch(err => console.log('Error', err))
โหยดีงามพระราม & นางสีดามากมาย แต่จะฟินกว่านี้ถ้า promisify นั้นมาพร้อมกับ Node เองเลย
รู้จักและใช้งาน promisify
พระเจ้าได้ยินเสียงคุณ จึงสถาปนาให้ Node เวอร์ชัน 8 มาพร้อมกับ promisify! เพื่อยกระดับความฟินคุณแค่เรียกใช้งานมันจาก util
ดังนี้
1const fs = require('fs')2// ข้าอยู่ตรงนี้3const { promisify } = require('util')4const readFile = promisify(fs.readFile)56readFile('./a.js', 'utf8')7 .then(text => readFile('./b.js', 'utf8')8 .then(text => //..)9 .catch(err => console.log('Error', err))
promisify ก็ใช้กับ Async / Await ได้นะ
เมื่อพูดถึง Promise ก็ขาดไม่ได้ที่จะยกตัวอย่างการใช้งานกับ Async / Await โค้ดของเราก็จะหน้าตาละมุนละไมหน่อย ดังนี้
1const fs = require('fs')2const { promisify } = require('util')3const readFile = promisify(fs.readFile)45async function doXxx() {6 try {7 const aContent = await readFile('./a.js', 'utf8')8 const bContent = await readFile('./b.js', 'utf8')9 // ...10 } catch (err) {11 console.log('Error', err)12 }13}1415doXxx()
Custom promisified functions
เราสามารถกำหนดให้ util.promisify()
คืนค่ากลับมาเป็นอะไรก็ได้ที่เรากำหนดขึ้นผ่าน util.promisify.custom
ดังนี้
1const util = require('util')23function foo() {4 return 'foo'5}6async function bar() {7 return 'bar'8}910foo[util.promisify.custom] = bar11console.log(util.promisify(foo) === bar) // true
สรุป
promisify
ไม่ใช่ของใหม่แต่พึ่งเกิดเป็นตัวเป็นตนใน API ของ Node เองก็ตอนเวอร์ชัน 8 นี่ละ เพื่อนๆคนไหนที่คันไม้คันมือทุกครั้งที่ต้องทนใช้ callback สไตล์โหนดๆ จะลองหยิบจับ promisify
มาใช้คู่กับ Promise บ้างก็กิ๊บเก๋ดีนะ
เอกสารอ้างอิง
Node.js v8.1.0 Documentation. Retrieved June, 13, 2017, from https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original
Dr. Axel Rauschmayer (2017). Node.js 8: util.promisify(). Retrieved June, 13, 2017, from http://2ality.com/2017/05/util-promisify.html
สารบัญ
- รู้จักและใช้งาน promisify
- promisify ก็ใช้กับ Async / Await ได้นะ
- Custom promisified functions
- สรุป
- เอกสารอ้างอิง