欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JS前端實(shí)現(xiàn)fsm有限狀態(tài)機(jī)實(shí)例詳解

 更新時(shí)間:2022年09月07日 17:20:07   作者:blazer  
這篇文章主要為大家介紹了JS前端實(shí)現(xiàn)fsm有限狀態(tài)機(jī)實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

我們平時(shí)開(kāi)發(fā)時(shí)本質(zhì)上就時(shí)對(duì)應(yīng)用程序的各種狀態(tài)進(jìn)行切換并作出相應(yīng)處理,最直接的方法就是添加標(biāo)志位然后考慮所有可能出現(xiàn)的邊界問(wèn)題,通過(guò)if...else if...else 來(lái)對(duì)當(dāng)前狀態(tài)進(jìn)行判斷從而達(dá)成頁(yè)面的交互效果, 但隨著業(yè)務(wù)需求的增加各種狀態(tài)也會(huì)隨之增多,我們就不得不再次修改if...else代碼或者增加對(duì)應(yīng)的判斷,最終使得程序的可讀性、擴(kuò)展性、維護(hù)性變得很麻煩

有限狀態(tài)機(jī),(英語(yǔ):Finite-state machine, FSM),又稱(chēng)有限狀態(tài)自動(dòng)機(jī),簡(jiǎn)稱(chēng)狀態(tài)機(jī),是表示有限個(gè)狀態(tài)以及在這些狀態(tài)之間的轉(zhuǎn)移和動(dòng)作等行為的數(shù)學(xué)模型。

利用有限狀態(tài)機(jī)我們可以將條件判斷的結(jié)果轉(zhuǎn)化為狀態(tài)對(duì)象內(nèi)部的狀態(tài),并且能夠使用對(duì)應(yīng)的方法,進(jìn)行對(duì)應(yīng)的改變。這樣方便了對(duì)狀態(tài)的管理也會(huì)很容易,也是更好的實(shí)踐了UI=fn(state)思想。

舉個(gè)栗子??

我們這里用一個(gè)簡(jiǎn)易的紅綠燈案例,實(shí)現(xiàn)一個(gè)簡(jiǎn)易的有限狀態(tài)機(jī),并且可以通過(guò)每一個(gè)狀態(tài)暴露出來(lái)的方法,改變當(dāng)前的狀態(tài)

const door = machine({
  RED: {
    yello: "YELLO",
  },
  GREEN: {
    red: "RED",
  },
  YELLO: {
    green: "GREEN",
  },
});
  • 首先初始時(shí)door的狀態(tài)顯示為紅燈即RED
  • 當(dāng)我們進(jìn)行yello操作的時(shí)候,狀態(tài)變成黃燈,即狀態(tài)改變?yōu)?code>YELLO
  • 當(dāng)我們進(jìn)行green操作的時(shí)候,狀態(tài)變成綠燈,即狀態(tài)改變?yōu)?code>GREEN
  • 當(dāng)我們連著進(jìn)行red操作、yello操作的時(shí)候,最終狀態(tài)變成黃燈,即狀態(tài)改變?yōu)?code>YELLO ...

從零開(kāi)始

通過(guò)接受一個(gè)對(duì)象(如果是函數(shù)就執(zhí)行),拿到初始值,并且在函數(shù)內(nèi)部維護(hù)一個(gè)變量記錄當(dāng)前的狀態(tài),并且記錄第一個(gè)狀態(tài)為初始狀態(tài)

const machine = statesObject => {
  if (typeof statesObject == "function") statesObject = statesObject();
  let currentState;
  for (const stateName in statesObject) {
    currentState = currentState || statesObject[stateName];
  }
};

獲取狀態(tài)

因?yàn)楫?dāng)前狀態(tài)是通過(guò)函數(shù)局部變量currentState進(jìn)行保存,我們需要一些方法

  • getMachineState:獲取當(dāng)前的狀態(tài)
  • getMachineEvents:獲取當(dāng)前狀態(tài)上保存了哪些方法

這兩個(gè)函數(shù)通過(guò)stateMachine進(jìn)行保存并作為函數(shù)結(jié)果進(jìn)行返回

const machine = statesObject => {
  let currentState
  ...
  const getMachineState = () => currentState.name;
  const getMachineEvents = () => {
    const events = [];
    for (const property in currentState) {
      if (typeof currentState[property] == "function") events.push(property);
    }
    return events;
  };
  const stateMachine = { getMachineState, getMachineEvents };
  ...
  return stateMachine
};

狀態(tài)改變

我們進(jìn)行改變的時(shí)候,調(diào)用的是一開(kāi)始配置好的方法對(duì)狀態(tài)進(jìn)行更改,此時(shí)需要將每一個(gè)狀態(tài)合并到stateStore中進(jìn)行保存

再將對(duì)應(yīng)的方法作為偏函數(shù)(函數(shù)預(yù)先將轉(zhuǎn)換的狀態(tài)和方法進(jìn)行傳遞),保存在stateMachinestateMachine會(huì)作為結(jié)果進(jìn)行返回),這樣就可以

  • 使用.yello().red()、.green()的方法,改變狀態(tài)
  • 使用.getMachineState().getMachineEvents()查看當(dāng)前狀態(tài)和查看當(dāng)前狀態(tài)對(duì)應(yīng)的方法
const machine = statesObject => {
  if (typeof statesObject == "function") statesObject = statesObject();
  let currentState;
  const stateStore = {};
  const getMachineState = () => currentState.name;
  const getMachineEvents = () => {
    const events = [];
    for (const property in currentState) {
      if (typeof currentState[property] == "function") events.push(property);
    }
    return events;
  };
  const stateMachine = { getMachineState, getMachineEvents };
  for (const stateName in statesObject) {
    stateStore[stateName] = statesObject[stateName];
    for (const event in stateStore[stateName]) {
      stateMachine[event] = transition.bind(null, stateName, event);
    }
    stateStore[stateName].name = stateName;
    currentState = currentState || stateStore[stateName];
  }
  return stateMachine;
};

transition

上面代碼中最重要的莫過(guò)于transition函數(shù),即改變當(dāng)前狀態(tài),在stateStore中獲取當(dāng)前的要更改的狀態(tài)名,重新給currentState賦值,并返回stateMachine供函數(shù)繼續(xù)鏈?zhǔn)秸{(diào)用

const machine = statesObject => {
  ...
  const transition = (stateName, eventName) => {
    currentState = stateStore[stateName][eventName];
    return stateMachine;
  };
  for (const stateName in statesObject) {
    stateStore[stateName] = statesObject[stateName];
    for (const event in stateStore[stateName]) {
      stateMachine[event] = transition.bind(null, stateName, event);
    }
    stateStore[stateName].name = stateName;
    currentState = currentState || stateStore[stateName];
  }
  return stateMachine;
};

看似沒(méi)有問(wèn)題,但是如果我們按照上面的代碼執(zhí)行后,獲得的狀態(tài)值為undefined,因?yàn)槲覀冊(cè)?code>getMachineState時(shí),獲取到的是currentState.name,而不是currentState,所以此時(shí)在獲取狀態(tài)的時(shí)候需要用通過(guò)函數(shù)進(jìn)行獲取obj => obj[xxx]

const machine = statesObject => {
  ...
  const transition = (stateName, eventName) => {
    currentState = stateStore[stateName][eventName](stateStore);
    return stateMachine;
  };
  for (const stateName in statesObject) {
    stateStore[stateName] = statesObject[stateName];
    for (const event in stateStore[stateName]) {
      const item = stateStore[stateName][event];
      if (typeof item == "string") {
        stateStore[stateName][event] = obj => obj[item];
        stateMachine[event] = transition.bind(null, stateName, event);
      }
    }
    stateStore[stateName].name = stateName;
    currentState = currentState || stateStore[stateName];
  }
  return stateMachine;
};

實(shí)現(xiàn)fsm狀態(tài)機(jī)

現(xiàn)在我們實(shí)現(xiàn)了一個(gè)完整的fsm,當(dāng)我們配置好狀態(tài)機(jī)時(shí)

const door = machine({
  RED: {
    yello: "YELLO",
  },
  GREEN: {
    red: "RED",
  },
  YELLO: {
    green: "GREEN",
  },
});

執(zhí)行如下操作時(shí),會(huì)打印我們想要的結(jié)果

  • door.getMachineState() --> RED
  • door.yello().getMachineState() --> YELLO
  • door.green().getMachineState() --> GREEN
  • door.red().yello().getMachineState() --> YELLO

實(shí)現(xiàn)鉤子函數(shù)

但是我們監(jiān)聽(tīng)不到狀態(tài)機(jī)的改變,所以當(dāng)我們想監(jiān)聽(tīng)狀態(tài)變換時(shí),應(yīng)該從內(nèi)部暴露出鉤子函數(shù),這樣可以監(jiān)聽(tīng)到狀態(tài)機(jī)內(nèi)部的變化,又能進(jìn)行一些副作用操作

對(duì)此,可以對(duì)transition進(jìn)行一些改造,將對(duì)于currentState狀態(tài)的改變用方法setMachineState去處理

setMachineState函數(shù)會(huì)攔截當(dāng)前狀態(tài)上綁定onChange方法進(jìn)行觸發(fā),并將改變狀態(tài)的函數(shù)、改變前的狀態(tài)改變后的狀態(tài)傳遞出去

const machine = statesObject => {
  ...
  const setMachineState = (nextState, eventName) => {
    let onChangeState;
    let lastState = currentState;
    const resolveSpecialEventFn = (stateName, fnName) => {
      for (let property in stateStore[stateName]) {
        if (property.toLowerCase() === fnName.toLowerCase()) {
          return stateStore[stateName][property];
        }
      }
    };
    currentState = nextState;
    onChangeState = resolveSpecialEventFn(lastState.name, "onChange");
    if (
      onChangeState &&
      typeof onChangeState == "function" &&
      lastState.name != currentState.name
    ) {
      onChangeState.call(
        stateStore,
        eventName,
        lastState.name,
        currentState.name
      );
    }
  };
  const transition = (stateName, eventName) => {
    const curState = stateStore[stateName][eventName](stateStore);
    setMachineState(curState, eventName);
    return stateMachine;
  };
  ...
  return stateMachine;
};

這樣我們?cè)谡{(diào)用時(shí),狀態(tài)的每一次改變都可以監(jiān)聽(tīng)到,并且可以執(zhí)行對(duì)應(yīng)的副作用函數(shù)

const door = machine({
  RED: {
    yello: "YELLO",
    onChange(fn, from, to) {
      console.log(fn, from, to, "onChange");
    },
  },
  GREEN: {
    red: "RED",
    onChange(fn, from, to) {
      console.log(fn, from, to, "onChange");
    },
  },
  YELLO: {
    green: "GREEN",
    onChange(fn, from, to) {
      console.log(fn, from, to, "onChange");
    },
  },
});

完整代碼

export default statesObject => {
  if (typeof statesObject == "function") statesObject = statesObject();
  let currentState;
  const stateStore = {};
  const getMachineState = () => currentState.name;
  const getMachineEvents = () => {
    let events = [];
    for (const property in currentState) {
      if (typeof currentState[property] == "function") events.push(property);
    }
    return events;
  };
  const stateMachine = { getMachineState, getMachineEvents };
  const setMachineState = (nextState, eventName) => {
    let onChangeState;
    let lastState = currentState;
    const resolveSpecialEventFn = (stateName, fnName) => {
      for (let property in stateStore[stateName]) {
        if (property.toLowerCase() === fnName.toLowerCase()) {
          return stateStore[stateName][property];
        }
      }
    };
    currentState = nextState;
    onChangeState = resolveSpecialEventFn(lastState.name, "onChange");
    if (
      onChangeState &&
      typeof onChangeState == "function" &&
      lastState.name != currentState.name
    ) {
      onChangeState.call(
        stateStore,
        eventName,
        lastState.name,
        currentState.name
      );
    }
  };
  const transition = (stateName, eventName) => {
    const curState = stateStore[stateName][eventName](stateStore);
    setMachineState(curState, eventName);
    return stateMachine;
  };
  for (const stateName in statesObject) {
    stateStore[stateName] = statesObject[stateName];
    for (const event in stateStore[stateName]) {
      const item = stateStore[stateName][event];
      if (typeof item == "string") {
        stateStore[stateName][event] = obj => obj[item];
        stateMachine[event] = transition.bind(null, stateName, event);
      }
    }
    stateStore[stateName].name = stateName;
    currentState = currentState || stateStore[stateName];
  }
  return stateMachine;
};

總結(jié)

這個(gè) fsm有限狀態(tài)機(jī) 主要完成了:

  • 狀態(tài)的可觀測(cè)
  • 狀態(tài)的鏈?zhǔn)秸{(diào)用
  • 狀態(tài)變化的鉤子函數(shù)

項(xiàng)目代碼:github.com/blazer233/a…

參考輪子:github.com/fschaefer/S…

以上就是JS前端實(shí)現(xiàn)fsm有限狀態(tài)機(jī)實(shí)例詳解的詳細(xì)內(nèi)容,更多關(guān)于JS前端fsm有限狀態(tài)機(jī)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論