Никогда больше не используйте если. Прототип, тест и код. КРАСНЫЙ-ЗЕЛЕНЫЙ-СИНИЙ, как говорит дядя Боб.

Введение

Конечные автоматы — полезная концепция в информатике и программировании, и они часто используются для моделирования поведения систем. В этом путешествии я исследую этот новый способ программирования, чтобы ответить на такие вопросы, как что такое конечные автоматы, как они работают, как я могу внедрить htem в свой рабочий процесс.

Конечный автомат — это математическая модель вычислений, которая представляет поведение системы как последовательность состояний и переходов между этими состояниями. В любой момент времени конечный автомат находится в определенном состоянии, и при выполнении определенных условий он может перейти в новое состояние. Конечные автоматы могут быть реализованы различными способами, например с помощью оператора switch или серии операторы if-else. С другой стороны, конечные автоматы позволяют абстрагировать методы/функции, охранники (если-иначе) и разработчики могут реализовать после определения конечного автомата логику системы. Раньше я говорил, что это промышленный способ программирования в противоположность ремесленной модели.

Библиотека XState — лучшая реализация конечных автоматов. Это идет еще дальше, поскольку они реализуют диаграммы состояний, где вы можете иметь события, дочерние конечные автоматы, параллельные состояния, например.

Поэтому я черпаю вдохновение в этой библиотеке, чтобы создать свою собственную единственную цель создания синхронной функции. Это единственное, чего не хватает в этой библиотеке.

Я изо всех сил стараюсь следовать синтаксису XState, чтобы вы могли использовать его с Stately Editor.

Функции

NB: Только для функций синхронизации

Если вы хотите использовать функции диаграммы состояний, такие как вложенные состояния, параллельные состояния, состояния истории, действия, вызываемые службы, отложенные переходы, временные переходы и т. д., используйте XState.

Быстрый старт

Монтаж

npm i @bemedev/fsf //or
yarn add @bemedev/fsf //or
pnpm add @bemedev/fsf

Использование (машина)

import { describe, expect, test } from 'vitest';
import { createLogic, interpret } from '@bemedev/fsf';

describe('#4: Complex, https query builder', () => {
  type Context = {
    apiKey?: string;
    apiUrl?: string;
    url?: string;
  };

  type Events = { products?: string[]; categories?: string[] };

  const queryMachine = createLogic(
    {
      schema: {
        context: {} as Context,
        // Add null option to make arguments optionals
        events: {} as Events | null,
        data: {} as string,
      },
      context: {},
      initial: 'preferences',
      states: {
        preferences: {
          always: {
            actions: ['setUrl', 'setApiKey', 'startUrl'],
            target: 'categories',
          },
        },
        categories: {
          always: [
            {
              cond: 'hasCategories',
              target: 'products',
              actions: 'setCategories',
            },
            'products',
          ],
        },
        products: {
          always: [
            {
              cond: 'hasProducts',
              target: 'final',
              actions: 'setProducts',
            },
            'final',
          ],
        },
        final: {
          data: 'query',
        },
      },
    },
    {
      strict: true,
      actions: {
        setApiKey: ctx => {
          ctx.apiKey = '123';
        },
        setUrl: ctx => {
          ctx.apiUrl = 'https://example.com';
        },
        startUrl: ctx => {
          const { apiUrl, apiKey } = ctx;
          ctx.url = `${apiUrl}?apikey=${apiKey}`;
        },
        setCategories: (ctx, { categories }) => {
          const _categories = categories?.join(',');
          ctx.url += `&categories=${_categories}`;
        },
        setProducts: (ctx, { products }) => {
          const _products = products?.join(',');
          ctx.url += `&categories=${_products}`;
        },
      },
      guards: {
        hasCategories: (_, { categories }) =>
          !!categories && categories.length > 0,
        hasProducts: (_, { products }) =>
          !!products && products.length > 0,
      },
      datas: {
        query: ctx => ctx.url,
      },
    },
  );

  const func = interpret(queryMachine);

  test('#1: no args', () => {
    // So here, arguments are optionals ! 
    expect(func()).toBe('https://example.com?apikey=123');
  });

  test('#2: categories', () => {
    expect(func({ categories: ['a', 'b'] })).toBe(
      'https://example.com?apikey=123&categories=a,b',
    );
  });

  test('#3: products', () => {
    expect(func({ products: ['a', 'b'] })).toBe(
      'https://example.com?apikey=123&categories=a,b',
    );
  });

  test('#4: categories and products', () => {
    expect(func({ products: ['a', 'b'], categories: ['c', 'd'] })).toBe(
      'https://example.com?apikey=123&categories=c,d&categories=a,b',
    );
  });
});

Примечание. Не используйте версию ниже 0.6.0, она нестабильна.