TPA & Alibaba Cloud ผนึกกำลัง สร้างสรรค์สัมมนาและอบรม ต่อยอดความร่วมมือสู่อนาคต
เมื่อวันที่ 21 พฤศจิกายน ที่ผ่านมา คุณเซฟ พงษ์ศิริ นายก […]
จากปัญหา Callback Hell ก็มีไลบรารี่ที่แก้ปัญหาความสวยงามของการเขียนโค้ดแบบ callback ของ JavaScript ออกมาหลายตัว ตัวที่น่าสนใจอีกตัวที่ผมชอบใช้ก็คือ async ซึ่งก็มีความสามารถมากมายสามารถกำหนดลำดับการทำงานแบบต่างๆได้มากมายไม่ว่าจะเป็น waterfall,series ,parallel,etc แต่ก็ยังไม่ตอบโจทย์เรื่องความสวยงามของโค้ด ก็มีการสร้างไลบรารี่ Promise ขึ้นมา เพื่อให้เขียนโค้ด asynchronous แต่ synchronous เป็นลำดับได้ เอ่ะ งง รึเปล่า เอาเป็นว่า Promise จะช่วยให้เราเข้าใจลำดับการทำงานของ asynchronous ที่มีความต่อเนื่องกันได้ง่ายขึ้น จริงรึเปล่าต้องมาลองกันดู
พื้นฐาน Promise
การเขียนฟังก์ชั่นแบบ Promise จะมี สถานะพื้นฐานอยู่ 3 สถานะคือ
* Promise ก็มีหลายคนพัฒนาออกมา ในแต่ละตัวก็อาจจะมีชื่อเรียกหรือ State ที่ต่างกันไป แต่โดยมาตรฐานวางโครงสร้างไว้ประมาณที่กล่าวมาข้างต้น
การเขียน Promise จะวางโครงสร้างโค้ด ดังนี้
function testPromise(a){ var deferred = Promise.pending(); if a == 1 deferred.resolve(a); else deferred.reject(a); return defered.promise; }
ใน Angular ก็มีไลบรารี่ Promise มาให้ด้วย ลักษณะการใช้งานก็จะคล้ายๆกันกับตัวอย่างโค้ดด้านบน แต่สถานะ pending จะเรียกชื่อต่างกัน คือ
var deferred = $q.defer();
ดังนั้นการเลือกใช้ Promise แต่ละตัวก็มีชื่อเรียกต่างๆกันไป ในตัวอย่างของ nodejs จะใช้ไลบรารี่ ชื่อ bluebird
หากยังไม่มีให้ทำการติดตั้ง
npm install bluebird
ขั้นตอน
var Promise = require('bluebird'); function car(){ var isStarted = false; this.start = function(){ var deferred = Promise.pending(); if(isStarted){ deferred.reject('Error:Already Started!'); } else{ isStarted = true; console.log('Started Engine'); deferred.resolve('Started Engine'); } return deferred.promise; } this.stop = function(){ var deferred = Promise.pending(); if(!isStarted){ deferred.reject('Error:Not Start yet!'); } else{ isStarted = false; console.log('Stopped Engine'); deferred.resolve('Stopped Engine'); } return deferred.promise; } this.forward=function(a){ console.log('forward',a); var deferred = Promise.pending(); if(!isStarted){ deferred.reject('Error:Not Start yet!'); } else{ isStarted = true; console.log('Go forward'); deferred.resolve('Go forward'); } return deferred.promise; } this.backward =function(){ var deferred = Promise.pending(); if(!isStarted){ deferred.reject('Error:Not Start yet!'); } else{ isStarted = true; console.log('Go backward'); deferred.resolve('Go backward'); } return deferred.promise; } this.turnleft=function(){ var deferred = Promise.pending(); if(!isStarted){ deferred.reject('Error:Not Start yet!'); } else{ isStarted = true; console.log('Turn left'); deferred.resolve('Turn left'); } return deferred.promise; } this.turnright=function(){ var deferred = Promise.pending(); if(!isStarted){ deferred.reject('Error:Not Start yet!'); } else{ isStarted = true; console.log('Turn right'); deferred.resolve('Turn right'); } return deferred.promise; } } var mycar = new car(); mycar.start() .then(mycar.turnleft) .then(mycar.turnright) .then(mycar.backward) .then(mycar.start).catch(function(e){ console.log(e); });
3. รันโปรแกรม
node ch8_promise.js
Started Engine Turn left Turn right Go backward Error:Already Started!
อธิบายโค้ด
ในโค้ดตัวอย่างนี้จะสร้าง ออบเจค car ซึ่งมีการเขียนโค้ดโดยใช้หลักการ OOP (Object Oriented Programming) คือจำลองโค้ดให้เหมือนวัตถุในโลกนี้ เพื่อใช้ในการสื่อสารความหมายให้เข้าใจพฤติกรรมการใช้งานโค้ดได้ง่ายขึ้น โดยส่วนตัวแล้วจะใช้เทคนิคออกแบบเหมือน grammar ภาษาอังกฤษ คือ
ซึ่งในตัวอย่างจะมีฟังก์ชั่น ดังนี้
car .start .stop .forward .backward .turnleft .turnright
เป็นการจำลองว่า รถมีฟังก์ชั่น start เครื่อง ดับเครื่อง เลี่ยวซ้าย เลี้ยวขวา เดินหน้า ถอยหลัง เหมือนการทำงานทั่วไปของรถจริงๆ ถ้าสังเกตในโค้ดจะมีการวางลำดับเหมือนกันทุกฟังก์ชั่น คือ
var deferred = Promise.pending(); deferred.resolve(result); deferred.reject(result);
ทั้งสามบรรทัดนั้นคือ 3 สถานะที่กล่าวมาแล้วข้างต้น
สำหรับ bluebird
เมื่อนำออบเจค car ไปใช้งาน
var mycar = new car(); mycar.start() .then(mycar.turnleft) .then(mycar.turnright) .then(mycar.backward) .then(mycar.start).catch(function(e){ console.log(e); });
เมื่อ start แล้ว ต้องการรันฟังก์ชั่นอื่นต่อ ให้ใช้ then ก็จะเห็นว่าโค้ดดูมีลำดับในการทำงาน ทำให้สื่อสารให้โปรแกรมเมอร์คนอื่นๆทำความเข้าใจได้ง่ายขึ้น ค่อยเข้าไปอ่านรายละเอียดปลีกย่อยในโค้ดอีกทีภายหลังได้ ถ้าเข้าใจขั้นตอนการทำงานชัดเจน
ค่าที่คืนมาจาก resolve จะคืนมากับฟังก์ชั่น then ส่วนค่าที่คืนมาจาก reject จะคืนมาที่ฟังก์ชั่น catch
ในตัวอย่างจะเกิด error ขึ้นหากมีการสั่ง start อีกครั้งเนื่องจาก รถได้ทำการ start ขึ้นมาแล้ว จึงเกิด error
และทำการ log ออกมาที่ฟังก์ชั่น catch
mycar.start() .then(mycar.turnleft)
หลังจาก then ของ start จะคืนค่า ‘Started Engine’ ใน then หากเขียนฟังก์ชั่น mycar.turnleft ก็จะได้รับค่า ‘Started Engine’ เข้าไป หากลองใส่โค้ดใน ฟังก์ชั่น turnleft ดังนี้
console.log(arguments);
ก็จะเห็นค่าของ ‘Started Engine’ ส่งเข้ามา
พบกับตอนที่ 9 การทำ Cluster