從任務中心看狀態機功能組件設計

本文經過設計一個簡單的任務中心來展現一個使用有限狀態機思想設計該如何作。前端

本文使用 JSDOC 添加註釋。git

關於有限狀態機

有限狀態機 (FSM) 維基 百度百科 是用來表示有限個狀態及在這些狀態之間轉移和動做的數學模型。早在07年就有文章講解 js 與狀態機的結合。github

在程序中,咱們用這種數學模型抽象業務的複雜行爲。做爲狀態機,有如下特徵特色:bash

  1. 事物擁有的狀態是有限的。
  2. 事物在任一時刻,只處在一種狀態。
  3. 能夠經過觸發事物的某些行爲,能夠致使事物從一種狀態過渡到另一種狀態。
  4. 一種行爲通常只能將其餘狀態變爲一種特定狀態,但不能將其變爲多種狀態。

設計實現組件

任務中心具備如下功能:架構

  • 任務中心能夠在任什麼時候刻加入新任務
  • 任務中心在啓動時纔會運行任務
  • 任務在任務中心運行時會被即時執行
  • 任務中心能夠被終止,暫停,恢復

若是針對以上需求,整理整個流程, 能夠認爲任務中心的流程以下:測試

由於需求中存在「能夠在任什麼時候間塞入任務」,因此對於不一樣的流程設定任務的不一樣執行狀態。ui

  1. 任務中心建立後便可容許塞入任務
  2. 任務中心運行狀態中,任何任務塞入會被即時執行
  3. 任務中心暫停或中止時,任何任務塞入也不會被執行
  4. 任務中心恢復運行時,會執行全部以前塞入並無被執行的任務

分離了任務中心和任務,這兩種並行的狀態,咱們就能夠分開設計,分開編碼了。編碼

那麼先作任務中心組件的實際設計,關於這個組件可能有的狀態以下:spa

  1. 初始狀態設計

  2. 運行狀態

  3. 暫停狀態

  4. 恢復狀態

  5. 恢復後的運行狀態

  6. 中止狀態

  7. 重啓狀態

........

但因爲咱們組件有的功能,咱們僅爲組件設計了四個狀態就能夠描述其整個生命流程:

  1. IDLE 初始化狀態
  2. RUNNING 運行狀態
  3. PAUSED 暫停狀態
  4. STOPPED 中止狀態

由於咱們的這個任務中心是給任務調度用的,因此僅跟任務有關的狀態纔是有用的狀態,這也是狀態機設計思惟的核心之一,僅設計使用相關聯的狀態, 並保證剩餘的狀態可成爲完整的流程, 任務中心的四個狀態組成流程以下圖所示:

實現組件

描述狀態

爲了更好的展現事務狀態,咱們在組件狀態切換中加入明確的狀態標識。並在類的狀態列表定義它們:

/** * 枚舉 InstantTask 組件狀態 * @enum {Number} * @readonly */
InstantTask.State = Object.freeze({
  /** * @memberof InstantTask.State * @type {Number} */
  IDLE: 0,
  /** * @memberof InstantTask.State * @type {Number} */
  RUNNING: 1,
  /** * @memberof InstantTask.State * @type {Number} */
  PAUSED: 2,
  /** * @memberof InstantTask.State * @type {Number} */
  STOPPED: 3,
});
複製代碼

描述行爲

組件一共存在五個行爲, 這些行爲在數學中能夠被認爲是變換器,它們就是上述狀態機運轉圖的具體行爲體現。

  1. start()

    用於啓動組件,設置組件爲 RUNNING 狀態。

    上一個狀態能夠是任何狀態。

    用於啓動任務中心,並會執行全部以前塞入並無被執行的任務

  2. pause()

    用於暫停組件,設置組件爲 PAUSED 狀態。

    上一個狀態只能是 RUNNING 狀態。

    用於暫停任務中心,在暫停後任何任務塞入也不會被執行

  3. resume()

    用於恢復組件的暫停狀態,設置組件爲 RUNNING 狀態

    上一個狀態只能是 PAUSED 狀態

    用於啓動任務中心,並會執行全部以前塞入並無被執行的任務,和start()之間功能差距不大,可是不會像start()同樣重置全部設定,並且主要用途是和pause方法作一一對應。一個方法或者狀態的多用途對於狀態機設計來講會增長架構複雜度,儘可能避免吧。

  4. stop()

    用於中止任務中心,設置組件爲 STOPPED 狀態

    上一個狀態只能是 RUNNING 狀態

    用於中止任務中心,和pause的區別是它會記錄中止狀態,而且不能被resume,和pause內部邏輯差距不大。主用途是和start()作對立方法。

  5. reset()

    用於重置任務中心,設置組件爲 IDLE 狀態

    上一個狀態能夠是任何狀態, 但主要是 STOPPED 狀態

    用於重置任務中心,此方法等於生成一個新的任務中心。

實現組件

當咱們肯定組件的抽象運行狀態和運行行爲後,就能夠結合實際業務邏輯來實現具體的組件了。 本文中使用 EventEmitter 來做爲事件的廣播和處理,前端也能夠借用 eventemitter3 這樣的近似的類實現一樣的功能。

同時接下來本文使用代碼中註釋的方式繼續說明每一個方法的詳細用途:

具體代碼已經放在: https://gist.github.com/Suixinlei/4b2da4ee1ef84e89b5cfccc1b88b3e4f

測試代碼放在: https://gist.github.com/Suixinlei/d8441babe5174b7b1d4326f39b0fcff2

測試結果以下所示,咱們能夠看到實際運行結果和咱們的抽象設計徹底一致,充分證實抽象設計對最終邏輯的影響:

添加任務 instant1
添加任務 instant2
任務中心開始運行
instant1 run
instant2 run
添加任務 instant3
instant3 run
任務中心已暫停
添加任務 instant4
恢復運行
instant4 run
複製代碼

拓展延伸

目前來講,全部任務都是即時任務,因此對於任務中心的狀態僅有四種便可知足需求。若是這些任務有一些是定時任務,甚至能夠循環執行並規定執行次數呢?讀完這篇文章我想你心中應該已經有些想法, 那麼看看下面的最終實現和你想的是否有些出入呢?

這裏是最終實現: https://gist.github.com/Suixinlei/e245812799fa160cdf0bbcdf279e68bc

總結

使用 FSM 設計組件能夠

  1. 讓組件變得邏輯清晰
  2. 組件邏輯和業務邏輯分離,從概念上有良好的分層結構
  3. 組件易測試,組件實現初期僅需測試組件邏輯部分而非複雜的業務邏輯
  4. 梳理組件邏輯閉環,能夠較容易的發現組件生命週期中缺乏的部分,反推業務進行改進

關注查看更多原創內容

關注公衆號投遞簡歷 (招聘視覺、交互、前端)

相關文章
相關標籤/搜索