1.背景介紹
? ? ? ?有限狀態(tài)機(jī),(英語:Finite-state machine, FSM),又稱為有限狀態(tài)自動機(jī),簡稱狀態(tài)機(jī),是一個(gè)數(shù)學(xué)模型。是一個(gè)抽象機(jī)器,在任何時(shí)候都可以處于有限數(shù)量的狀態(tài)之一。響應(yīng)某些外部輸入,F(xiàn)SM可以從一個(gè)狀態(tài)轉(zhuǎn)換到另一個(gè)狀態(tài);從一種狀態(tài)到另一種狀態(tài)的變化稱為過渡。狀態(tài)機(jī)的行為可以在現(xiàn)代社會中的許多設(shè)備中觀察到,這些設(shè)備根據(jù)它們呈現(xiàn)的事件序列執(zhí)行預(yù)定的一系列行為。例如自動售貨機(jī),當(dāng)存放適當(dāng)?shù)挠矌沤M合時(shí)分配產(chǎn)品,當(dāng)車輛等待時(shí)改變順序的交通燈等。
? ? ? ?有限狀態(tài)機(jī)最初應(yīng)用在數(shù)字系統(tǒng)的涉及,它對數(shù)字系統(tǒng)的設(shè)計(jì)具有十分重要的作用。有限狀態(tài)機(jī)是指輸出取決于過去輸入部分和當(dāng)前輸入部分的時(shí)序邏輯電路。,除了輸入部分和輸出部分外,有限狀態(tài)機(jī)還含有一組具有“記憶”功能的寄存器,這些寄存器的功能是記憶有限狀態(tài)機(jī)的內(nèi)部狀態(tài),它們常被稱為狀態(tài)寄存器。
? ? ? ?在有限狀態(tài)機(jī)中,狀態(tài)寄存器的的下一個(gè)狀態(tài)不僅與輸入信號有關(guān),而且還與該寄存器的當(dāng)前狀態(tài)有關(guān),因此有限狀態(tài)機(jī)又可以認(rèn)為是組合邏輯和寄存器邏輯的一種組合。其中,寄存器邏輯的功能是存儲有限狀態(tài)機(jī)的內(nèi)部狀態(tài);而組合邏輯又可以分為次態(tài)邏輯和輸出邏輯兩部分,次態(tài)邏輯的功能是確定有限狀態(tài)機(jī)的下一個(gè)狀態(tài),輸出邏輯的功能是確定有限狀態(tài)機(jī)的輸出。
? ? ? ? 在我們前端開發(fā)中,我們可以套用有限狀態(tài)機(jī)模型,將業(yè)務(wù)流程狀態(tài)化,劃分狀態(tài)和相應(yīng)的觸發(fā)事件與動作,利用生 命周期事件進(jìn)行控制與執(zhí)行。
一個(gè)可以用狀態(tài)機(jī)模擬的簡單機(jī)制的例子---旋轉(zhuǎn)門。

? ? ? ? ? 常見的旋轉(zhuǎn)門是控制通往地鐵和游樂園的游樂設(shè)施的旋轉(zhuǎn)門。在腰部高度有三個(gè)旋轉(zhuǎn)臂,一個(gè)在入口處。最初狀態(tài),手臂被鎖住了,阻止進(jìn)入,防止顧客通過。將硬幣或代幣存放在旋轉(zhuǎn)門上的一個(gè)槽中,可以打開手臂,讓一位顧客穿過。顧客通過后,手臂再次被鎖定,直到另一枚硬幣被插入。作為一個(gè)狀態(tài)機(jī),旋轉(zhuǎn)門有兩種可能的狀態(tài):鎖定和解鎖。有兩種可能的輸入影響其狀態(tài):將硬幣放入槽(硬幣)并推動手臂(推)。在鎖定狀態(tài)下,推臂不起作用;無論輸入推送有多少次,它都處于鎖定狀態(tài)。投入一枚硬幣-也就是給機(jī)器一個(gè)硬幣輸入-將狀態(tài)從“鎖定”轉(zhuǎn)換為“解鎖”。在解鎖狀態(tài)下,放入額外的硬幣不起作用;那就是增加硬幣輸入不會改變狀態(tài)。但是,一個(gè)推著手臂的顧客,將推送這個(gè)動作輸入,狀態(tài)就轉(zhuǎn)回到鎖定狀態(tài)。

? ? ? ? 旋轉(zhuǎn)門狀態(tài)機(jī)也可以用稱為狀態(tài)圖(上圖)的有向圖表示 。每個(gè)狀態(tài)都由一個(gè)節(jié)點(diǎn)(圓圈)表示。邊(箭頭)顯示從一個(gè)狀態(tài)到另一個(gè)狀態(tài)的轉(zhuǎn)換。每個(gè)箭頭都標(biāo)有觸發(fā)該轉(zhuǎn)換的輸入。不會導(dǎo)致狀態(tài)改變的輸入(例如在解鎖狀態(tài)下的硬幣輸入)由返回到原始狀態(tài)的圓形箭頭表示。從黑點(diǎn)到鎖定節(jié)點(diǎn)的箭頭表示這是初始狀態(tài)。
2.知識剖析
2.1:有限狀態(tài)機(jī)有什么特點(diǎn)
有限狀態(tài)機(jī)fsm一般有以下特點(diǎn):
*)可以用狀態(tài)來描述事物,并且任一時(shí)刻,事物總是處于一種狀態(tài);
*)事物擁有的狀態(tài)總數(shù)是有限的;
*)通過觸發(fā)事物的某些行為,可以導(dǎo)致事物從一種狀態(tài)過渡到另一種狀態(tài);
*)同一種行為,可以將事物從多種狀態(tài)變成同種狀態(tài),但是不能從同種狀態(tài)變成多種狀態(tài)。
2.2:有限狀態(tài)機(jī)的組成
2.2.1、狀態(tài)機(jī)由一組狀態(tài)和轉(zhuǎn)換組成例如:
need-to-insert-img
狀態(tài):固體 、 液體 、 氣體。
轉(zhuǎn)換 :融化 、汽化 、 冷凝 、 凍結(jié)。

2.2.2、有限狀態(tài)機(jī)形式(有限狀態(tài)機(jī)長什么樣)
1、需要的函數(shù)庫:javascript-state-machine插件。
2、生成實(shí)例,創(chuàng)建生命周期:
var fsm = new StateMachine({
init: 'solid',
transitions: [
{name: 'Melt', from: 'solid', to: 'liquid'},
{name: 'Vaporize', from: 'liquid', to: 'gas'},
{name: 'Condense', from: 'gas', to: 'liquid'},
{name: 'Freeze', from: 'liquid', to: 'solid'}
],
methods: {
onBeforeMelt:? ? ? ? function() { /* ... */ },
onBeforeVaporize:? ? function() { /* ... */ },
onBeforeCondense:? ? function() { /* ... */ },
onBeforeFreeze:? ? ? function() { /* ... */ },
onLeaveSolid:? ? ? ? function() { /* ... */ },
onLeaveLiquid:? ? ? ? function() { /* ... */ },
onLeaveGas:? ? ? ? ? function() { /* ... */ },
onEnterLiquid:? ? ? ? function() { /* ... */ },
onEnterGas:? ? ? ? ? function() { /* ... */ },
onEnterSolid:? ? ? ? function() { /* ... */ },
onAfterMelt:? ? ? ? ? function() { /* ... */ },
onAfterVaporise:? ? ? function() { /* ... */ },
onAfterCondense:? ? ? function() { /* ... */ }
onAfterFreeze:? ? ? ? function() { /* ... */ }
}
});
//方法調(diào)用
//1,自執(zhí)行方法:
fsm.onMelt();
fsm.onVaporize();
fsm.onCondense();
fsm.onFreeze();
//1、觸發(fā)調(diào)用方式:
fsm.Melt();
fsm.Vaporize();
fsm.Condense();
fsm.Freeze();

HTML代碼:

有限狀態(tài)機(jī)包含以下基本內(nèi)容:
1、初始狀態(tài)init:init選項(xiàng)用來表示fsm對象的初始狀態(tài),
2、轉(zhuǎn)換規(guī)則transitions:transitions選項(xiàng)用來描述fsm對象所有狀態(tài)的變化規(guī)則,每一種變化規(guī)則對應(yīng)一種行為。
3、方法methods:methods方法為實(shí)例的每一種行為都添加了一個(gè)方法,調(diào)用這個(gè)方法就相當(dāng)于觸發(fā)對象的某種行為,當(dāng)對象行為發(fā)生時(shí),對象的狀態(tài)就可以發(fā)生變化。
如以上例子創(chuàng)建的實(shí)例將擁有如下行為方法:
fsm.Melt() :調(diào)用該方法,實(shí)例狀態(tài)將從'solid'變?yōu)?liquid'
fsm.Freeze() :調(diào)用該方法,實(shí)例狀態(tài)將從'liquid'變?yōu)?solid'
fsm.Vaporize() :調(diào)用該方法,實(shí)例狀態(tài)將從'liquid'變?yōu)?gas'
fsm.Condense() :調(diào)用該方法,實(shí)例狀態(tài)將從'gas'變?yōu)?liquid'
2.2.3、有限狀態(tài)機(jī)的方法
Javascript Finite State Machine允許為每個(gè)事件指定兩個(gè)自定義方法,以Melt事件為例:
onbeforeMelt:在warn事件發(fā)生之前觸發(fā)
onafterMelt:在warn事件發(fā)生之后觸發(fā)。
每個(gè)狀態(tài)指定兩個(gè)自定義方法,以solid狀態(tài)為例:
onleaveSolid:在離開solid狀態(tài)時(shí)觸發(fā)
onenterLiquid:在進(jìn)入liquid狀態(tài)時(shí)觸發(fā)。
2.2.4、通用的生命周期事件
為了在發(fā)生轉(zhuǎn)換時(shí)跟蹤或執(zhí)行操作,有以下五個(gè)通用的生命周期事件:
// onBeforeTransition -在任何轉(zhuǎn)換之前觸發(fā)
// onLeaveState -離開任何狀態(tài)被觸發(fā)
// onTransition -在任何過渡期間被觸發(fā)
// onEnterState -進(jìn)入任何狀態(tài)被觸發(fā)
// onAfterTransition -任何轉(zhuǎn)換后觸發(fā)
2.2.5、特定的轉(zhuǎn)換和狀態(tài)
除了通用事件之外,還可以使用特定的轉(zhuǎn)換和狀態(tài)來觀察轉(zhuǎn)換:
// onBefore-在特定的轉(zhuǎn)換之前觸發(fā)
// onBefore-在特定的轉(zhuǎn)換之前觸發(fā)
// onAfter-在特定的TRANSITION后觸發(fā)
// onLeave-離開特定的狀態(tài)觸發(fā)
// onEnter-進(jìn)入特定狀態(tài)觸發(fā)
// on-簡寫onAfter
// on-簡寫onEnter
2.2.6、輔助方法:
// fsm.is(s) -如果狀態(tài)s是當(dāng)前狀態(tài),則返回true
// fsm.can(t) -如果t從當(dāng)前狀態(tài)發(fā)生轉(zhuǎn)換,則返回true
// fsm.cannot(t) -如果t從當(dāng)前狀態(tài)不能發(fā)生轉(zhuǎn)換,則返回true
// fsm.transitions() -返回當(dāng)前狀態(tài)允許的轉(zhuǎn)換列表
// fsm.allTransitions() -返回所有可能的轉(zhuǎn)換的列表
// fsm.allStates() -返回所有可能狀態(tài)的列表
//Cancelling a Transition取消轉(zhuǎn)換
2.2.7、可以通過false在以下任何生命周期事件中顯式返回來取消轉(zhuǎn)換
在方法中return false可以取消當(dāng)前觸發(fā)的行為:
// onBeforeTransition
// onBefore
// onLeaveState
// onLeave
// onTransition
//所有隨后的生命周期事件將被取消,狀態(tài)將保持不變。
3.常見問題
如何使用有限狀態(tài)機(jī)
4 解決方案
首先創(chuàng)建fsm實(shí)例----設(shè)置初始初始狀態(tài)------規(guī)定轉(zhuǎn)換規(guī)則------定義方法。
var fsm = new StateMachine({? ? ? ? ? //創(chuàng)建fsm實(shí)例
init: 'solid',? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //設(shè)置初始狀態(tài)
transitions: [? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//定義裝換規(guī)則
{name: 'Melt', from: 'solid', to: 'liquid'},
{name: 'Vaporize', from: 'liquid', to: 'gas'},
{name: 'Condense', from: 'gas', to: 'liquid'},
{name: 'Freeze', from: 'liquid', to: 'solid'}
],
methods: {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //定義方法
onBeforeMelt:? ? ? ? function() { /* ... */ },
onLeaveSolid:? ? ? ? function() { /* ... */ },
onEnterLiquid:? ? ? ? function() { /* ... */ },
onAfterMelt:? ? ? ? ? function() { /* ... */ },
}
});
//觸發(fā)轉(zhuǎn)換
fsm.Melt();
fsm.Vaporize();
fsm.Condense();
fsm.Freeze();
6.擴(kuò)展思考
Javascript Finite State Machine 的異步轉(zhuǎn)換
有時(shí),我們需要在狀態(tài)轉(zhuǎn)換期間執(zhí)行一些異步代碼,并確保在代碼完成之前不會輸入新狀態(tài)。
舉個(gè)栗子:
當(dāng)我們從一個(gè)狀態(tài)轉(zhuǎn)換出來并想逐漸淡入一個(gè)UI組件,或者將它從屏幕上滑出來,而且不想在動畫完成之后轉(zhuǎn)換到下一個(gè)狀態(tài)。就可以通過 從任何生命周期事件中返回Promise對象來實(shí)現(xiàn)此目的。從生命周期事件返回Promise將導(dǎo)致該轉(zhuǎn)換的生命周期暫停??梢岳^續(xù)解決,也可以拒絕承諾。
7.參考文獻(xiàn)
一、官方文檔https://github.com/jakesgordon/javascript-state-machine/blob/master/docs/states-and-transitions.md