Очистка и остановка порожденных актеров из службы в Xstate - PullRequest
3 голосов
/ 03 апреля 2020

Я внимательно прочитал документы, однако есть некоторые части, которые не совсем ясны, и большинство из них связано с актерами / службами.

Я немного неясен в деталях появившихся актеров. жизненные циклы.

  • Будут ли звонить .stop() на службу с порожденными актерами, также .stop() порожденными актерами или их оставят в покое?
  • Как мне убирать порожденных актеров от машина? Есть ли способ доступа к порожденным актерам в children изнутри самой службы? (как внутри действия)

Скажем, у меня есть машина с действием для добавления файлов. Когда файл добавляется, для него вызывается новый spawn(), и ссылка сохраняется в context. Теперь, когда машина завершает свою работу, я хочу сбросить context и очистить children после .stop() каждого из них.

Чтобы быть полностью определенным c здесь - это то, как я смоделировал систему загрузки, которая реализует поведение, описанное выше.

В этой реализации всякий раз, когда машина возвращается к idle состояние, я сбрасываю context и вручную .stop() каждый порожденный актер. Однако службы актеров все еще находятся в .children, и я не могу получить к ним доступ изнутри действий машины (добавление 3-го параметра meta к resetContext не приводит ни к чему с доступом к текущему children).

Относительно .stop() актеров и очистки children и сброса context, в идеале я хотел бы иметь отдельное действие для каждого из них, которое будет выполняться при входе в состояние idle, однако поскольку мне кажется, что я не могу найти способ доступа к children, единственное решение, о котором я могу подумать, - это сделать все за одно действие через context.

Также следует отметить, что в официальный пример при удалении задачи ее созданный актер не является .stop() ed, что заставляет меня задуматься, если это недосмотр или есть причина?

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

// upload system machine

const uploadSystemMachine = Machine(
  {
    id: 'upload-system',
    initial: 'idle',
    context: {
      files: [],
      successes: 0,
      failures: 0,
      cancellations: 0,
    },
    states: {
      idle: {
        entry: ['clearSpawnedActors', 'resetContext'],
        on: {
          OPEN: 'active',
        },
      },
      active: {
        initial: 'waitingForFiles',
        states: {
          waitingForFiles: {
            on: {
              CLOSE: {
                target: '#upload-system.idle',
              },
              ADD: {
                actions: 'addFile',
              },
              UPLOAD: {
                target: 'pending',
                cond: 'hasFiles',
              },
            },
          },
          pending: {
            entry: 'startFileUploads',
            on: {
              'FILE.UPLOADED': {
                actions: 'incrementSuccesses',
              },
              'FILE.FAILED': {
                actions: 'incrementFailures',
              },
              'FILE.CANCELLED': {
                actions: 'incrementCancellations',
              },
              CANCEL: {
                actions: 'cancelFileUpload',
              },
              '': {
                target: 'completed',
                cond: 'allSettled',
              },
            },
          },
          completed: {
            type: 'final',
            on: {
              CLOSE: '#upload-system.idle',
            },
          },
        },
      },
    },
    on: {
      KILL: '.idle',
    },
  },
  {
    guards: {
      hasFiles: context => {
        return context.files.length > 0;
      },
      hasNoFiles: context => {
        return context.files.length === 0;
      },
      allSettled: context => {
        return (
          context.files.length === context.successes + context.failures + context.cancellations
        );
      },
    },
    actions: {
      startFileUploads: context => {
        context.files.forEach(actor => {
          actor.send('START');
        });
      },
      addFile: assign({
        files: (context, event) => {
          const newValue = spawn(
            fileMachine.withContext(event.target ? event.target.data : {}),
            context.files.length
          );

          return [...context.files, newValue];
        },
      }),
      resetContext: assign({
        files: context => {
          context.files.forEach(actor => {
            console.log(actor);
            actor.stop();
          });

          return [];
        },
        successes: 0,
        failures: 0,
        cancellations: 0,
      }),
      cancelFileUpload: (context, event) => {
        const fileID = event.data.id;
        for (let i = 0; i < context.files.length; i++) {
          if (context.files[i].id === fileID) {
            context.files[i].send('CANCEL');
            break;
          }
        }
      },
      incrementSuccesses: assign({
        successes: context => {
          return context.successes + 1;
        },
      }),
      incrementFailures: assign({
        failures: context => {
          return context.failures + 1;
        },
      }),
      incrementCancellations: assign({
        cancellations: context => {
          return context.cancellations + 1;
        },
      }),
    },
  }
);
// file machine

const fileMachine = Machine(
  {
    id: 'file',
    initial: 'idle',
    context: null,
    states: {
      idle: {
        on: {
          START: 'pending',
        },
      },
      pending: {
        invoke: {
          id: 'upload',
          src: 'upload',
        },
        on: {
          RESOLVED: 'success',
          REJECTED: 'failed',
          CANCEL: 'cancelled',
        },
      },
      success: {
        type: 'final',
        entry: [
          () => {
            console.log('%centering file success state', 'color: green');
          },
          sendParent('FILE.UPLOADED'),
        ],
      },
      failed: {
        type: 'final',
        entry: [
          () => {
            console.log('%centering file failed state', 'color: red');
          },
          sendParent('FILE.FAILED'),
        ],
      },
      cancelled: {
        type: 'final',
        entry: [
          () => {
            console.log('%centering file cancelled state', 'color: orange');
          },
          sendParent('FILE.CANCELLED'),
        ],
      },
    },
  },
  {
    services: {
      upload: (__context, event) => {
        return callback => {
          let cancelRequest;
          let settled;
          const req = new Promise((resolve, reject) => {
            cancelRequest = reject;
            console.log('Started uploading', event);

            return setTimeout(() => {
              const cond = Math.random() > 0.5;

              if (cond) {
                resolve();
              } else {
                reject();
              }
            }, Math.random() * 5000);
          });

          req
            .then(() => {
              settled = true;
              console.log('%cFile uploaded successfully', 'color: green');
              callback('RESOLVED');
            })
            .catch(() => {
              settled = true;
              console.log('%cFile failed to upload', 'color: red');
              callback('REJECTED');
            });

          return () => {
            if (!settled) {
              console.log('canceling request');
              cancelRequest();
            }
          };
        };
      },
    },
  }
);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...