8

我已经广泛阅读了文档,但是仍有一些部分不完全清楚,并且大多数与演员/服务有关

我对生成的演员生命周期的细节有点模糊。

  • 使用生成的演员调用.stop()服务也会.stop()生成演员还是被挂起?
  • 我应该如何从机器中清除生成的演员?有没有办法children从服务本身内部访问生成的演员?(比如从动作内部)

假设我有一台带有添加文件操作的机器。添加文件时spawn(),会为其调用一个新文件,并将引用存储到context. 现在,当机器完成它正在做的任何事情时,我想重置context并清除每一个children之后的内容。.stop()

在这里要完全具体的是我如何对实现上述行为的上传系统进行建模。

在这个实现中,每当机器返回idle状态时,我都会重置context并手动设置.stop()每个生成的actor。但是,actor 服务仍然在 中徘徊,.children我无法从机器操作内部访问它们(将第三个参数添加meta到 theresetContext不会导致任何可以访问 current 的东西children)。

关于.stop()演员并清除children和重置context,理想情况下,我希望每个动作都有一个单独的动作,在idle进入状态时运行,但是由于我似乎无法找到访问方法children,因此通过一个动作完成所有操作这context是我能想到的唯一解决方案。

另外,值得注意的是,在删除待办事项的官方示例.stop()中,它生成的演员没有被编辑,这让我想知道这是疏忽还是有原因?

下面,为方便起见,是实现上传系统和文件的代码。完整的实现,也包括 vizualizer,可以在这里找到:

// 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();
            }
          };
        };
      },
    },
  }
);
4

0 回答 0