-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[do not merge] Allow wildcard transitions to act as "error handlers" #2935
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1770,8 +1770,6 @@ describe('invoke', () => { | |
}); | ||
|
||
it('should call onError only on the state which has invoked failed service', () => { | ||
let errorHandlersCalled = 0; | ||
|
||
const errorMachine = Machine({ | ||
initial: 'start', | ||
states: { | ||
|
@@ -1784,44 +1782,48 @@ describe('invoke', () => { | |
type: 'parallel', | ||
states: { | ||
first: { | ||
invoke: { | ||
src: () => () => { | ||
throw new Error('test'); | ||
}, | ||
onError: { | ||
target: 'failed', | ||
cond: () => { | ||
errorHandlersCalled++; | ||
return false; | ||
initial: 'waiting', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this test was weirdly written so I've refactored it in the process, this should land on the main branch regardless of this PR - I will create a branch with just this simple adjustment later on |
||
states: { | ||
waiting: { | ||
invoke: { | ||
src: () => () => { | ||
throw new Error('test'); | ||
}, | ||
onError: { | ||
target: 'failed' | ||
} | ||
} | ||
} | ||
}, | ||
failed: {} | ||
} | ||
}, | ||
second: { | ||
invoke: { | ||
src: () => () => { | ||
// empty | ||
}, | ||
onError: { | ||
target: 'failed', | ||
cond: () => { | ||
errorHandlersCalled++; | ||
return false; | ||
initial: 'waiting', | ||
states: { | ||
waiting: { | ||
invoke: { | ||
src: () => () => { | ||
// empty | ||
}, | ||
onError: { | ||
target: 'failed' | ||
} | ||
} | ||
} | ||
}, | ||
failed: {} | ||
} | ||
}, | ||
failed: { | ||
type: 'final' | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
|
||
interpret(errorMachine).start().send('FETCH'); | ||
const service = interpret(errorMachine).start(); | ||
service.send('FETCH'); | ||
|
||
expect(errorHandlersCalled).toEqual(1); | ||
expect(service.state.value).toEqual({ | ||
fetch: { first: 'failed', second: 'waiting' } | ||
}); | ||
}); | ||
|
||
it('should be able to be stringified', () => { | ||
|
@@ -2641,6 +2643,33 @@ describe('invoke', () => { | |
}) | ||
.start(); | ||
}); | ||
|
||
it('a wildcard transition should be able to receive an error event', (done) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a new test case that only passes with the introduced implementation change. It initially made sense to me to introduce this change in a vein that "error transitions are just transitions". However, after thinking about it:
TLDR. it should be decided how this should behave as part of the statelyai/rfcs#4 . Note that the first argument still stands, it's not that obvious that Thoughts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeaaah, I think that |
||
const machine = createMachine({ | ||
initial: 'fetching', | ||
states: { | ||
fetching: { | ||
invoke: { | ||
src: () => Promise.reject(new Error('test err')), | ||
onDone: { | ||
target: 'complete' | ||
} | ||
}, | ||
on: { | ||
'*': { | ||
actions: () => { | ||
done(); | ||
} | ||
} | ||
} | ||
}, | ||
complete: { | ||
type: 'final' | ||
} | ||
} | ||
}); | ||
interpret(machine).start(); | ||
}); | ||
}); | ||
|
||
it('invoke `src` should accept invoke source definition', (done) => { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that both the current logic (the one on the main branch) and the proposed logic in this PR sound incorrect to me, regardless of the decisions that have to be made in response to the other comment.
Incorrect scenarios are really edge-case-y though and applicable more in v5 context than in v4. This whole if statement might be also dropped entirely as part of the work resulting from The Errors RFC so it's not worth addressing this now.
With the current logic, based on
nextEvents
, this won't quite work correctly when we consider parallel regions because a singleonError
in one of the regions "turns off" this if statement for another parallel region.The proposed logic feels incorrect because we can create such structure:
As we might see here - the inner error handler is forbidden but the outer one is not (the latter doesn't quite matter but maybe other edge cases can be created using such setup). This would return false from
state.can(errorEvent)
when in thea
state but theonError: undefined
looks explicit enough to count this as an "explicit" error handler. It feels that it would make sense to swallow errors with such a forbiddenonError
- note that it's not obvious how to express "error swallowing" differently. The only thing that comes to my mind is this:And I wouldn't count this as intuitive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added v5 essential label to this PR just to remember to incorporate thoughts from this comment above. The PR itself has to be redone.