Babel Coder

Node.js 8: แปลง callback เป็น Promise ด้วย util.promisify

beginner

ปัญหาหนึ่งของการใช้งาน JavaScript นั่นก็คือ Callback Hell ที่จะทำให้คุณเร่าร้อนดั่งไฟนรกทุกครั้งที่ต้องอ่านโค้ด โดยเฉพาะอย่างยิ่งกับ callback สไตล์ฉบับนู๋ Node.js นึกภาพไม่ออก? ลองดูโค้ดข้างล่างดูฮะ

const fs = require('fs')
fs.readFile('./a.js', 'utf8', (err, text) => {
  if(err) {
    console.log('Error', err)
  } else {
    // ทำอะไรซักอย่างกับข้อมูลที่ได้จากไฟล์ a แล้วค่อยอ่านไฟล์ b ต่อ
    fs.readFile('./b.js', 'utf8', (err, text) => {
      if(err) onsole.log('Error', err)
      else { //... }
    })
  }
})

ใน 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 ได้ เช่น

const fs = require('fs')
const Promise = require('bluebird')
const readFile = Promise.promisify(fs.readFile)

readFile('./a.js', 'utf8')
  .then(text => readFile('./b.js', 'utf8')
  .then(text => //..)
  .catch(err => console.log('Error', err))

โหยดีงามพระราม & นางสีดามากมาย แต่จะฟินกว่านี้ถ้า promisify นั้นมาพร้อมกับ Node เองเลย

รู้จักและใช้งาน promisify

พระเจ้าได้ยินเสียงคุณ จึงสถาปนาให้ Node เวอร์ชัน 8 มาพร้อมกับ promisify! เพื่อยกระดับความฟินคุณแค่เรียกใช้งานมันจาก util ดังนี้

const fs = require('fs')
// ข้าอยู่ตรงนี้
const { promisify } = require('util')
const readFile = promisify(fs.readFile)

readFile('./a.js', 'utf8')
  .then(text => readFile('./b.js', 'utf8')
  .then(text => //..)
  .catch(err => console.log('Error', err))

promisify ก็ใช้กับ Async / Await ได้นะ

เมื่อพูดถึง Promise ก็ขาดไม่ได้ที่จะยกตัวอย่างการใช้งานกับ Async / Await โค้ดของเราก็จะหน้าตาละมุนละไมหน่อย ดังนี้

const fs = require('fs')
const { promisify } = require('util')
const readFile = promisify(fs.readFile)

async function doXxx() {
  try {
    const aContent = await readFile('./a.js', 'utf8')
    const bContent = await readFile('./b.js', 'utf8')
    // ...
  } catch(err) {
    console.log('Error', err)
  }
}

doXxx()

Custom promisified functions

เราสามารถกำหนดให้ util.promisify() คืนค่ากลับมาเป็นอะไรก็ได้ที่เรากำหนดขึ้นผ่าน util.promisify.custom ดังนี้

const util = require('util')

function foo() { return 'foo' }
async function bar() { return 'bar' }

foo[util.promisify.custom] = bar
console.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


แสดงความคิดเห็นของคุณ


No any discussions