我已经广泛阅读了文档,但是仍有一些部分不完全清楚,并且大多数与演员/服务有关
我对生成的演员生命周期的细节有点模糊。
- 使用生成的演员调用
.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();
}
};
};
},
},
}
);