Skip to main content

Background Commands

Hosanna implements a robust system for handling background commands and asynchronous operations through its AsyncCommand framework. This documentation explains how background commands work and how to use them effectively in your application.

Live Example

Ui And Interaction Background Commands

@view('UiAndInteractionBackgroundCommands')
export class UiAndInteractionBackgroundCommandsView extends BaseExampleScreenView<UiAndInteractionBackgroundCommandsState> {

  @state stateText = '';
  @state result = '';

  protected getViews(): ViewStruct<ViewState>[] {

    // --- edit below ---

    return [
      VGroup([
        Button({ text: 'Dispatch Background HTTP' })
          .isInitialFocus(true)
          .onClick(() => this.dispatchBackgroundHttp()),
        Label({ text: `State: ${this.stateText}` }),
        Label({ text: this.result, width: 1000, wrap: true }),
      ]).itemSpacing(16)
        .translation([50, 50])
    ]

    // --- edit above ---
  }

  private dispatchBackgroundHttp() {
    this.stateText = 'loading';
    this.result = '';
    const url = 'https://jsonplaceholder.typicode.com/posts/2';
    return this.dispatch<IHsFetchResponse>(HttpMethod.Get, { url })
      .then((response) => {
        this.stateText = 'completed';
        this.result = JSON.stringify(response.json);
      })
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      .catch((reason: any) => {
        this.stateText = 'failed';
        this.result = String(reason);
      });
  }
}
Try It

Dispatch a background command and watch the UI handle progress and completion.

Core Components

1. AsyncCommand

The AsyncCommand class is the fundamental building block for background operations in Hosanna. It provides:

  • Unique command identification
  • Lifecycle management (dispatch, cancel, pause, resume)
  • Progress tracking
  • State management
  • Promise-based execution

2. Command Structure

Each background command consists of:

  • ID: A unique identifier for tracking

  • Scope: The command's operational context

  • Handler: The specific operation handler

  • Arguments: Optional parameters for the command

  • State: Current execution state

  • Progress: Completion progress (0.0 to 1.0)

    Using Background Commands

1. Creating a Command Handler

@commandCategory('DataLoader')
class LoadDataHandler {
@command('LoadJsonData')
loadJsonData(payload: any): HsPromise<any> {
// Implement async operation
}
}

2. Dispatching Commands

You can dispatch background commands from views using:

this.dispatchAsync('DataLoader', 'LoadJsonData', { url: '/data.json' })
.then((result) => {
// Handle success
})
.catch((error) => {
// Handle error
});

Passing function pointers across tasks

Sometimes you need to pass a function to be executed in a background task. Use AsyncFunctionPointer (module + export name) to reference the function safely across node/task boundaries.

Critical: Must Be Exported Functions

AsyncFunctionPointer cannot be inline or anonymous functions — they must be exported functions defined at module scope. The function must be exported so it can be serialized as a string pointer and resolved across boundaries.

// Correct: export the function first
// transformFunctions.ts
export function processRows(response: IHsFetchResponse) {
// ... processing logic
}

// Then import and use it
import { processRows } from './transformFunctions';

this.dispatch<IHsFetchResponse>(Http.Get, {
url: 'https://api.example.com/data',
transformFunction: processRows // Exported function reference
});

Signature:

// hosanna-api
declare type AsyncFunctionPointer = (...args: any[]) => any;
declare function hs_resolveAsyncFunctionPointer(ptr: AsyncFunctionPointer): Function;

The runtime can retrieve the function later (e.g., via a helper like getAsyncFunctionFromPointer) and execute it inside a task, avoiding closure capture issues.

Linter Diagnostics

Hosanna's linter reports a diagnostic if you pass an inline function or use .bind(this) where an AsyncFunctionPointer is expected (for example, HsFetchOptions.transformFunction or postProcessFunction). Export the function at module scope and pass that exported symbol instead.

Command Lifecycle

Background commands go through several states:

  1. Initializing: Command is being created
  2. Running: Command is actively executing
  3. Paused: Command execution is temporarily suspended
  4. Completed: Command has finished successfully
  5. Failed: Command encountered an error
  6. Cancelled: Command was manually cancelled

Integration with Async Framework

Hosanna's background commands integrate with:

  • AsyncManager: Central controller for async tasks
  • HsPromise: Custom promise implementation
  • TimerService: Manages timing and scheduling
  • PortPromise: Platform-specific promise implementation (e.g., for Roku)

Best Practices

  1. Error Handling

    • Always implement proper error handling in command handlers
    • Use try/catch blocks for potential failures
    • Provide meaningful error messages
  2. Progress Tracking

    • Update progress for long-running operations
    • Use progress callbacks for UI updates
  3. Resource Management

    • Properly clean up resources when commands complete
    • Cancel long-running commands when no longer needed
  4. Platform Considerations

    • Use platform-specific implementations when necessary
    • Consider memory constraints on different devices

Example Implementation

// Define a background command handler
@commandCategory('MediaLoader')
class MediaLoadHandler {
@command('LoadVideo')
async loadVideo(payload: {url: string}): HsPromise<void> {
try {
// Initialize progress
this.progress = 0;

// Perform async operation
const result = await fetch(payload.url);

// Update progress
this.progress = 0.5;

// Process result
const videoData = await result.json();

// Complete operation
this.progress = 1.0;
return videoData;
} catch (error) {
throw new Error(`Failed to load video: ${error.message}`);
}
}
}

// Using the command in a view
class VideoPlayerView extends BaseView {
loadVideo(url: string) {
this.dispatchAsync('MediaLoader', 'LoadVideo', { url })
.then(videoData => this.playVideo(videoData))
.catch(error => this.handleError(error));
}
}

Debugging Background Commands

For debugging background commands, Hosanna provides:

  • Command execution logging
  • State tracking
  • Progress monitoring
  • Error reporting

You can use the Remote Debug Panel to monitor and debug background commands during development.