Hey, great work on the updated README! But I'm still left a little confused about best practice.
The README says the following, with my bolding for emphasis:
If the target state is "", then
this.emit("TargetHandlerName")should be called. For any other states,this.emitWithState("TargetHandlerName")must be called instead.
Here's some code from the examples:
const newSessionHandlers = {
'NewSession': function() {
(...)
then
const startGameHandlers = Alexa.CreateStateHandler(states.STARTMODE, {
'NewSession': function () {
this.emit('NewSession'); // Uses the handler in newSessionHandlers
},
and later
const guessModeHandlers = Alexa.CreateStateHandler(states.GUESSMODE, {
'NewSession': function () {
this.handler.state = '';
this.emitWithState('NewSession'); // Equivalent to the Start Mode NewSession handler
},
In both of the second two snippets, it looks to me like the intent is to send the user back to the start mode session handler for it to handle what should be done. I get that bit. But the way it is done conflicts with the notes about should and must in the docs.
Is it a case that actually either are valid? In which case, for clarity, maybe pick one and stick to it? Or if both are indeed valid, then change the wording from should and must to can be.
States are complex enough as it is! Anything to simplify or clarify is good. Or is it my mis-reading or misunderstanding of the docs here?
Hi @talkingnews,
This part of readme is actually not very accurate. I'll try to explain the difference between emit and emitWithState here and have task setup to update the doc later.
Firstly, it's important to distinguish the purpose of using intent forwarding combined with the concept of state.
In some cases (for example: NewSession), we may have similar logic that we want to use in different state ('same welcome speech in both '' and 'start'') but we don't want to write the same code in different handlers. We can use this.emit({handlerName}) in 'start' handler to invoke that function in '' handler. Notice that in this case, we are NOT switching the context of state, so any subsequent requests will still hit the handler in 'start' state.
In other cases, you want the skill to switch state after it receives certain request (for example: user said yes to some question and skill are moving to the another state. ). We then need to forward that request to handler in another state as well as switching the state by using
JavaScript
this.handler.state = {targetState};
this.emitWithState({targetHandler})
In this example, we switches the state so any subsequent request will be picked up by handlers in the update state.
With this in mind, let's look at the underlying logic of this.emit. Alexa-SDK is event driven and uses EventEmitter to route requests to handler. All handlers are registered as listeners by their name. Since all handlers are in parallel with each other in the EventEmitter (alexa object), we distinguish them by appending the state name to end of the handler name. So NewSession in '' state will have name "NewSession" whereas NewSession in Start state will have name "NewSessionStart". this.emit({handlerName}) will then invoke the handler with that name.
this.emitWithState({handlerName}) is a helper function we provide that essentially does the "appending" logic for developers. Take the following code for example:
JavaScript
this.emit('NewSessionStart');
and
JavaScript
this.handler.state = 'start'
this.emitWithState('NewSession')
They both invoke the same handler (NewSession in start state). But the first one doesn't change the state context whereas the second one switch the state to start
Revisiting the sample code in Readme, now we can see that NewSession in the startGameHandlers just invoke the handler in newSessionHandlers without changing the state value. NewSession in the guessModeHandlers invoke the same handler as well as changing the start back to ''.
I understand that this is a bit confusing to developers. We are working on a new design that will separate these two types of logic more clearly. Please let me know if you have any questions.
Regards
Thanks @Amazon-tianrenz - that helped, and I think more clarity about this in the docs would be welcome by many developers. It is, as you say, a bit confusing. Especially when a lot of the example code given doesn't follow this format. But I can see the docs are always evolving, and it's nice to know the developers are listening to input! Thanks
I just came here to ask about the same thing. I hope this section of the docs can be improved.
Also, the docs don't really explain what NewSession is. Why do we need NewSession handlers in each state? In other words, why would NewSession be called when there's a value for state? Isn't the state empty at the beginning of a session?
Firstly, it's important to distinguish the purpose of using intent forwarding combined with the concept of state.
Actually this messes up the understanding and readability of the code for the skill. Looking at an event handler it is hard to tell if it was triggered by Alexa, internally by the sdk, or forwarded from another state.
In some cases (for example: NewSession), we may have similar logic that we want to use in different state
There is already a programming concept that solves this problem, it is called functional programming. You should put shared code in a function and call the same function. No need to use complicated event forwarding for this.
Hi all,
Thanks for your support for the SDK. We've released ASK SDK v2 for Node.js yesterday, which aims to address many of the issues here. We encourage you to check out our wiki here.
Most helpful comment
Hi @talkingnews,
This part of readme is actually not very accurate. I'll try to explain the difference between
emitandemitWithStatehere and have task setup to update the doc later.Firstly, it's important to distinguish the purpose of using intent forwarding combined with the concept of state.
In some cases (for example:
NewSession), we may have similar logic that we want to use in different state ('same welcome speech in both''and'start'') but we don't want to write the same code in different handlers. We can usethis.emit({handlerName})in'start'handler to invoke that function in''handler. Notice that in this case, we are NOT switching the context ofstate, so any subsequent requests will still hit the handler in'start'state.In other cases, you want the skill to switch state after it receives certain request (for example: user said yes to some question and skill are moving to the another state. ). We then need to forward that request to handler in another state as well as switching the
stateby usingJavaScript this.handler.state = {targetState}; this.emitWithState({targetHandler})In this example, we switches the state so any subsequent request will be picked up by handlers in the update state.
With this in mind, let's look at the underlying logic of
this.emit. Alexa-SDK is event driven and usesEventEmitterto route requests to handler. All handlers are registered as listeners by their name. Since all handlers are in parallel with each other in theEventEmitter(alexa object), we distinguish them by appending the state name to end of the handler name. SoNewSessionin''state will have name "NewSession" whereasNewSessioninStartstate will have name "NewSessionStart".this.emit({handlerName})will then invoke the handler with that name.this.emitWithState({handlerName})is a helper function we provide that essentially does the "appending" logic for developers. Take the following code for example:JavaScript this.emit('NewSessionStart');and
JavaScript this.handler.state = 'start' this.emitWithState('NewSession')They both invoke the same handler (
NewSessioninstartstate). But the first one doesn't change the state context whereas the second one switch the state tostartRevisiting the sample code in Readme, now we can see that
NewSessionin thestartGameHandlersjust invoke the handler innewSessionHandlerswithout changing the state value.NewSessionin theguessModeHandlersinvoke the same handler as well as changing the start back to''.I understand that this is a bit confusing to developers. We are working on a new design that will separate these two types of logic more clearly. Please let me know if you have any questions.
Regards