Asynchronous Method Dispatch (AMD) in JavaScript

JavaScript is single-threaded, therefore it is very important to consider how servant implementation decisions affect the overall application. In a JavaScript program with a graphical user interface, spending too much time in the implementation of a Slice operation can delay the processing of UI events and adversely impact the user experience.

Asynchronous Method Dispatch (AMD), the server-side equivalent of AMI, allows a server to receive a request but then suspend its processing. When processing resumes and the results are available, the server sends a response explicitly using a callback object provided by the Ice run time.

AMD is transparent to the client, that is, there is no way for a client to distinguish a request that, in the server, is processed synchronously from a request that is processed asynchronously.

One use case that requires AMD is nested invocations. Since all outgoing invocations have asynchronous semantics, an operation implementation whose results depend on the outcome of a nested invocation must postpone its response until the nested invocation completes.

On this page:

Using AMD in JavaScript

To use asynchronous dispatch, the operation implementation must return a promise object.

The implementation can return an instance of the standard Promise type or any other type that provides a then method.

For example, suppose we define the following operation:

Slice
interface I
{
    int foo(short s, out long l);
}

The servant method for operation foo should have this signature:

JavaScript
foo(s, current) { ... }
JavaScript
foo(s:string, current:Ice.Current):PromiseLike<[number, Ice.Long]> { ... }

It should return a promise object that, when fulfilled, returns an array containing an integer and a long.

AMD Exceptions in JavaScript

There are two processing contexts in which the logical implementation of an AMD operation may need to report an exception: the dispatch context (the call sequence that invokes the servant method), and the context of the promise (the call sequence that rejects the promise).

These are not necessarily two different contexts: it is legal to return a rejected promise from the dispatch context.

It is legal for the implementation to raise an exception instead of returning a rejected promise.

AMD Example in JavaScript

To demonstrate the use of AMD in Ice, consider an operation that must make a nested invocation:

Slice
module Demo
{
    exception RequestFailed
    {
        string reason;
    }

    interface TaxManager
    {
        float computeTax(float amount)
            throws RequestFailed;
    }

    interface ShoppingCart
    {
        float computeTotal()
            throws RequestFailed;
    }
}

In this over-simplified example, a shopping cart object must query a tax manager object in order to compute the total amount owed by the customer.

Our servant class derives from Demo.ShoppingCart and supplies a definition for the computeTotal method:

JavaScript
class ShoppingCartI extends Demo.ShoppingCart
{
    constructor()
    {
        this._subtotal = 0.0;
    }
 
    computeTotal(current)
    {
        const taxManager = ... // get TaxManager proxy
        const subtotal = this._subtotal;
        return taxManager.computeTax(subtotal).then(
            tax => subtotal + tax;
            ex => {
                if(ex instanceof Demo.RequestFailed)
                {
                    throw ex; // Relay RequestFailed exception from computeTax
                }
                else
                {
                    throw new Demo.RequestFailed("failed: " + ex.toString());
                }
            });
    }
}
TypeScript
class ShoppingCartI extends Demo.ShoppingCart
{
    constructor()
    {
        this._subtotal = 0.0;
    }
 
    computeTotal(current:Ice.Current):PromiseLike<number>
    {
        const taxManager = ... // get TaxManager proxy
        const subtotal = this._subtotal;
        return taxManager.computeTax(subtotal).then(
            tax => subtotal + tax;
            ex => {
                if(ex instanceof Demo.RequestFailed)
                {
                    throw ex; // Relay RequestFailed exception from computeTax
                }
                else
                {
                    throw new Demo.RequestFailed("failed: " + ex.toString());
                }
            });
    }


    _subtotal:number;
}


The implementation of computeTotal makes a nested invocation of computeTax and returns a promise whose resolve and reject functions return the result of the computeTotal dispatch.

See Also