[Node.js] How to use node.js workflow 狀態機

最早開始寫 Node.js 時候,很多事件需要做到順序性,這些順序性,可以從畫面操作可能會出現狀況來做例子,例如 : 輸入表單 => 驗證欄位是否有錯 => 預覽 => 送出儲存,也因為 Javascript Event driven 特性,這時候就會使用到 Callback 方法或是寫一大堆的條件判斷式

A(function() {  
  B(function() {  
      C(function() {  
          D();  
      });  
  });  
});  

但過多的 Call back function,又要去做到 event handling 往往會造成 call back hell,如下所示:

fs.readdir(source, function(err, files) {  
  if (err) {
    console.log('Error finding files: ' + err)
  } else {
    files.forEach(function(filename, fileIndex) {
      console.log(filename)
      gm(source + filename).size(function(err, values) {
        if (err) {
          console.log('Error identifying file size: ' + err)
        } else {
          console.log(filename + ' : ' + values)
          aspect = (values.width / values.height)
          widths.forEach(function(width, widthIndex) {
            height = Math.round(width / aspect)
            console.log('resizing ' + filename + 'to ' + height + 'x' + height)
            this.resize(width, height).write(destination + 'w' + width + '_' + filename, function(err) {
              if (err) console.log('Error writing file: ' + err)
            })
          }.bind(this))
        }
      })
    })
  }
})

Node.js EventEmitter 神器

因此本篇文章主要分享如何利用 Node.js API 本身有支援 EventEmitter 的功能,來處理一連串的狀態,並且避免 Call back hell 問題出現,要實作此方法其實很簡單,步驟如下所示 :

  • Step 1. 將模組引入,並將類別實例化
var events = require('events');  
var workflow = new events.EventEmitter();  
  • Step 2. 宣告 workflow outcome 物件
workflow.outcome = {  
    success: false,
    errors: [],
    errfor: {}
  };
  • Step 3. 新增處理錯誤狀態的函數

使用者在輸入表單如有任何錯誤,可以透過此函數做檢查並回應錯誤訊息

workflow.hasErrors = function() {  
    return Object.keys(workflow.outcome.errfor).length !== 0 || workflow.outcome.errors.length !== 0;
  };

  workflow.on('exception', function(err) {
    workflow.outcome.errors.push('Exception: '+ err);
    return workflow.emit('response');
  });

  workflow.on('response', function() {
    workflow.outcome.success = !workflow.hasErrors();
    res.send(workflow.outcome);
  });

  return workflow;
};
  • Step 4. 開始設定workflow狀態檢查

下列是一個基本處理表單輸入、驗證、儲存的功能,其狀態圖如下:

程式實作如下所示 :

workflow.on('validate', function() {  
        if (!req.body.title) workflow.outcome.errfor.title = '必填';
        if (!req.body.startDate) workflow.outcome.errfor.startDate = '必填';

        //return if we have errors already
        if (workflow.hasErrors()) {
            workflow.outcome.errors.push('有欄位尚未填寫');
            return workflow.emit('response');
        }

        return workflow.emit('saveNewPost');
    });

    workflow.on('savePost', function() {
        var fieldsToSet = {
            organizer: req.body.organizer.trim(),
            title: req.body.title.trim(),
            url: req.body.url.trim(),
            typeTags: req.body.typeTags.split(','),
            startDate: req.body.startDate,
            userCreated: {
                name: 'Default',
                time: new Date().toISOString()
            }
        };

        new req.app.db.models.Post(fieldsToSet).save(function(err) {
            if (err) return workflow.emit('exception', err);
            return workflow.emit('response');
        });
    });

    return workflow.emit('validate');
};

Ben Shiue

Having being a full stack engineer. His interests in Node.js, ARM mbed, IoT solutions. Contact us : [email protected]

ALL RIGHTS RESERVED. COPYRIGHT © 2016. Designed and Coded by Makee.io