LearnNewsExamplesServices

RPC Layer

Seamless Communication Across Threads and Beyond

In a multi-threaded architecture like Neo.mjs, efficient and seamless communication between different execution contexts (Web Workers, Main Thread, and even backend services) is paramount. The Neo.mjs Remote Procedure Call (RPC) Layer provides a powerful abstraction that simplifies this complex inter-thread and inter-process communication, allowing developers to invoke methods on remote objects as if they were local.

Abstracting Cross-Worker Communication

At its core, the RPC Layer eliminates the need for developers to manually handle postMessage calls, message parsing, and promise resolution when communicating between Web Workers. Whether it's your App Worker interacting with the VDom Worker to update the UI, or with the Data Worker to fetch and process information, the RPC Layer provides a clean, promise-based API.

Benefit: This abstraction significantly reduces boilerplate code and cognitive load. Developers can focus on the business logic of their application rather than the intricacies of message passing between threads. This leads to faster development, more readable code, and fewer errors related to inter-thread communication.

Extending to Backend Integration

The power of the Neo.mjs RPC Layer extends beyond just inter-worker communication within the browser. It provides a consistent mechanism for interacting with backend services. This means the same patterns and mental model used for communicating with a Data Worker can be applied to making API calls to your server.

Benefit: A unified approach to both frontend inter-thread communication and backend integration streamlines the development process. It reduces the learning curve for new team members and ensures consistency in how data and commands are exchanged across the entire application stack.

Key Advantages:

  • Simplicity: Invoke remote methods with a simple function call, receiving a promise that resolves with the result. The RPC Layer handles serialization, deserialization, and message routing automatically.
  • Reduced Boilerplate: Eliminates the need for manual message listeners, dispatchers, and complex state management around asynchronous operations.
  • Improved Readability & Maintainability: Code becomes cleaner and easier to understand, as the underlying communication mechanism is abstracted away.
  • Performance: Designed for efficiency, the RPC Layer ensures that inter-thread communication is as performant as possible, minimizing overhead and contributing to the overall responsiveness of Neo.mjs applications.
  • Error Handling: Provides robust error propagation across thread boundaries, making it easier to debug and handle issues that arise during remote method invocations.

Conceptual Example: Consistent API for Internal and External Calls

Imagine calling a method on a data service defined within the Data Worker realm (e.g., MyApp.data.UserService) from your App Worker:

// In your App Worker code
const userData = await MyApp.data.UserService.fetchUser(userId);
console.log(userData);

Now, consider triggering a backend request using the same RPC pattern, as seen in apps/colors/view/ViewportController.mjs:

// In apps/colors/view/ViewportController.mjs
response = await Colors.backend.ColorService.read({
    amountColors : stateProvider.getData('amountColors'),
    amountColumns: stateProvider.getData('amountColumns'),
    amountRows   : stateProvider.getData('amountRows')
});

Notice how the syntax for invoking a method on a backend service (Colors.backend.ColorService.read) is virtually identical to invoking a method on an internal worker service (Neo.worker.Data.getService('UserService').fetchUser). This consistency is a core strength of the Neo.mjs RPC Layer.

Behind the scenes, for both internal and external remote calls, the RPC Layer handles:

  1. Serializing the method call and arguments.
  2. Sending a message to the target (worker or backend).
  3. The target receiving the message, invoking the method.
  4. Serializing the result and sending it back.
  5. Resolving the promise in the caller with the received data.

This powerful abstraction is a cornerstone of Neo.mjs's multi-threaded architecture, enabling developers to build complex, highly responsive, and scalable applications with remarkable ease.