Skip to content

Instantly share code, notes, and snippets.

@send2moran
Created March 21, 2020 12:40
Show Gist options
  • Save send2moran/bf6eb2796cf746ab3a3e3183f45604e8 to your computer and use it in GitHub Desktop.
Save send2moran/bf6eb2796cf746ab3a3e3183f45604e8 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
const autoSaveMachine = Machine({
id: 'autosave',
initial: 'idle',
context: {
value: null,
revId: 0,
errorRetryLevels: [10, 30, 300],
errorRetryLevelIdx: 0,
errorCounter: 0
},
on: {
DOCUMENT_UPDATE: {
action: ctx => {
console.log('mivan')
}
}
},
states: {
idle: {
on: {
DOCUMENT_UPDATE: 'saving'
}
},
saving: {
type: 'parallel',
entry: 'incrementRevision',
states: {
request: {
states: {
pending: {},
completed: {}
},
initial: 'pending',
invoke: {
id: 'request',
src: 'save',
onDone: '.completed',
onError: '#autosave.serviceerror'
}
},
debounce: {
states: {
debouncing: {
invoke: {
id: 'debounce',
src: 'debounce',
onDone: 'settled'
}
},
debouncerestart: {
on: {
'': 'debouncing'
}
},
settled: {}
},
initial: 'debouncing',
on: {
DOCUMENT_UPDATE: '.debouncerestart'
}
},
dirtiness: {
states: {
clean: {},
dirty: {}
},
initial: 'clean',
on: {
DOCUMENT_UPDATE: '.dirty'
}
}
},
on: {
'': [
{
target: 'idle',
cond: 'saveFinished'
},
{
target: 'saving',
cond: 'saveFinishedDirty'
}
]
}
},
serviceerror: {
id: 'serviceerror',
entry: 'resetRetryInterval',
invoke: {
id: 'timer',
src: 'timer'
},
states: {
counting: {
entry: 'initServiceErrorCounter',
on: {
'TICK': {
actions: 'decrementCounter'
},
'FORCE_RETRY': {
target: 'retry',
actions: 'resetRetryInterval',
},
'': {
cond: 'retryCounterTimeup',
actions: 'levelUpRetryInterval',
target: 'retry',
}
}
},
retry: {
invoke: {
id: 'request',
src: 'save',
onDone: '#autosave.saving',
onError: 'counting'
}
}
},
initial: 'counting'
}
}
}, {
actions: {
incrementRevision: assign({
revId: context => context.revId + 1
}),
decrementCounter: assign({
errorCounter: ctx => ctx.errorCounter - 1
}),
initServiceErrorCounter: assign({
errorCounter: ctx =>
ctx.errorRetryLevels[ctx.errorRetryLevelIdx]
}),
resetRetryInterval: assign({
errorRetryLevelIdx: ctx => 0
}),
levelUpRetryInterval: assign({
errorRetryLevelIdx: ctx =>
Math.min(
ctx.errorRetryLevelIdx + 1,
ctx.errorRetryLevels.length - 1
)
})
},
guards: {
saveFinished: (context, event, meta) =>
meta.state &&
meta.state.matches({
saving: {
request: 'completed',
debounce: 'settled',
dirtiness: 'clean'
}
}),
saveFinishedDirty: (context, event, meta) =>
meta.state &&
meta.state.matches({
saving: {
request: 'completed',
debounce: 'settled',
dirtiness: 'dirty'
}
}),
retryCounterTimeup: ctx =>
ctx.errorCounter === 0,
},
services: {
save: (context, event) =>
new Promise((resolve, reject) => {
setTimeout(resolve, 2000)
}),
debounce: (context, event) =>
new Promise(resolve => {
setTimeout(resolve, 500)
}),
timer: (context, event) =>
(callback, onEvent) => {
const id = setInterval(
() => callback('TICK'),
1000
)
return () => clearInterval(id)
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment