Response Handling in ESW-TS
This section helps to understand how responses have been modeled in TMT architecture and, also how they can be handled at UI application side.
If you have not gone the Services Section, we recommend you to check Services out first. Assuming you have read through the Services section and what all types of response can be returned by the Service of your current interest.
The Response types are Union types/ADT’s. All of them are tagged by _type
field, this field provides information at compile time to developers of all possible response variation at runtime for the current response.
- Typescript
-
source
// response handlers const onCancelled = () => { // do something on receiving Cancelled type } const onCompleted = () => { // do something on receiving Completed type } const onStarted = () => { // do something on receiving Started type } const onError = () => { // do something on receiving Error type } const onInvalid = () => { // do something on receiving Invalid type } const onLocked = () => { // do something on receiving Locked type } const prefix = new Prefix('ESW', 'component_1') const componentId = new ComponentId(prefix, 'Assembly') const commandService: CommandService = await CommandService(componentId, { tokenFactory }) const setup = new Setup(prefix, 'move', []) const submitResponse: SubmitResponse = await commandService.submit(setup) // Handle all variations (Exhaustive switch pattern) switch (submitResponse._type) { case 'Cancelled': onCancelled() break case 'Completed': onCompleted() break case 'Started': onStarted() break case 'Error': onError() break case 'Invalid': onInvalid() break case 'Locked': onLocked() break } // or handle few types switch (submitResponse._type) { case 'Completed': onCompleted() break case 'Started': onStarted() break case 'Error': onError() break default: // !!important!! // do something by default for other cases }
All non-ADT or Normal response types (for ex: Done
, LogMetadata
, etc.) does not require extra effort of handling it with switch cases. Those models do not have any discriminatory field like _type
and there will always be one variation, and the information about all fields inside model will be statically known.
Asynchronous Programming
You may have notice that we have used async-await
syntax in all examples provided in the different pages of this documentation. However, one may still want to write using combinators provided by Promise, since most of the ESW-TS service methods are Promise based.
Promise is provided by Javascript language,Using which we can write asynchronous programs in a more manageable way. Using Async/Await syntax, a promise-based asynchronous code can be written in a synchronous format which saves a lot of time and code becomes scalable. We recommend using async-await syntax for writing asynchronous code as it increases readability of the code. When using callbacks
or combinators
on top of Promise, issues like callback hell
emerges over time.
For instance, following example showcases two scenarios without async-await syntax and how it would look like
- Sending submit commands using CommandService in sequence
- Sending submit commands using CommandService in parallel
- Typescript
-
source
const prefix = new Prefix('ESW', 'component_1') const componentId = new ComponentId(prefix, 'HCD') CommandService(componentId) .then((commandService) => { const setup1 = new Setup(prefix, 'move', []) const setup2 = new Setup(prefix, 'rotate', []) const setup3 = new Setup(prefix, 'set', []) const responsePromise = commandService.submit(setup1) //sequential way to handle multiple submit calls responsePromise .then((submitResponse1) => { handleResponse(submitResponse1) commandService.submit(setup2).then((submitResponse2) => { handleResponse(submitResponse2) commandService.query(submitResponse2.runId).then(fetchNewESWState) }) }) .catch(handleError) // parallel way to handle multiple promises const responsePromise3 = commandService.submit(setup3) Promise.all([responsePromise, responsePromise3]).then((submitResponses) => { submitResponses.forEach(handleResponse) const lastResponse = submitResponses.pop() if (lastResponse) { commandService.query(lastResponse.runId).then(fetchNewESWState) } }) }) .catch((err) => { handleError(err) }) const handleError = (error: Error) => { // See Error handling section for in general details throw new Error(error.message) } const handleResponse = (response: SubmitResponse) => { // See Response handling section for in general details console.log(response) } const fetchNewESWState = () => { // fetch new state from backend }
The following example showcases the same above scenarios with async-await syntax.
- Sending multiple submit commands with async-await syntax
- Typescript
-
source
const handleError = (error: Error) => { // See Error handling section for details throw new Error(error.message) } const handleResponse = (response: SubmitResponse) => { // See Response handling section for more details console.log(response) } const prefix = new Prefix('ESW', 'component_1') const componentId = new ComponentId(prefix, 'HCD') try { const commandService = await CommandService(componentId) const setup1 = new Setup(prefix, 'move', []) const setup2 = new Setup(prefix, 'rotate', []) const setup3 = new Setup(prefix, 'set', []) //sequential way to handle multiple submit calls await commandService.submit(setup1) const responsePromise2 = await commandService.submit(setup2) const queryResponse = await commandService.query(responsePromise2.runId) handleResponse(queryResponse) // parallel way to handle multiple promises const responsePromise3 = commandService.submit(setup3) const responsePromise4 = commandService.submit(setup1) const submitResponses = await Promise.all([responsePromise3, responsePromise4]) submitResponses.forEach(handleResponse) const lastResponse = submitResponses.pop() if (lastResponse) { const queryRes = commandService.query(lastResponse.runId) } } catch (err) { handleError(err as Error) }
We strongly recommend to use async-await while writing asynchronous code in Javascript.