Fake a GWT Synchronous RPC call

2019-01-19 22:05发布

问题:

First of all, I know that doing a synchronous call is "wrong", and know that "is not possible".

But, in a situation a lot complex (i dont know how to explain), i need to wait the response from server, I'am using the GWT-Platform command implementation for the GWT RPC calls.

I was looking for some kind of "hack" for doing this.

Thanks in advance.

回答1:

There is a solution but it is not easy (e.g. you cannot flip a single parameter to make it work). GWT is using normal JS XMLHttpRequest under the hood. In GWT there is an overlay type for it called com.google.gwt.xhr.client.XMLHttpRequest. This class is used to send requests to the server over HTTP. Each JS XMLHttpRequest is first initialized by calling method open. This method has few parameters, but the third parameter specifies if the request should be asynchronous. If you change it to false, request will be synchronous.

But GWT-RPC doesn't use this class directly, it using it via RpcRequestBuilder, and this class is not using XMLHttpRequest directly as well, it is using RequestBuilder.

So what you'll need to do is to create customized version of RpcRequestBuilder and RequestBuilder (which will use XMLHttpRequest initialized to be synchronous).

The you can set RPCRequest builder to your GWT-RPC service instance, by casting it to the ServiceDefTarget.

Do you still want to have synchronous GWT-RPC requests?



回答2:

Usually, by handling stuff in the onSuccess() function of your RPC request, you'll automatically "wait the response from server". So I assume you want to block all the code currently running? Since JavaScript is single-threaded that won't be easy, there is no sleep function that just halts the program.

But it might be that a hack using a timer does what you want:

    Timer checkRPCResponse = new Timer() {
        @Override
        public void run() {
            if (!serverResponseReceived) {
                this.schedule(100);
            } else {
                proceedWithProgram();
            }
        }
    };
    checkRPCResponse.schedule(100);

I haven't tried out if the this.schedule(100) works in the above example, but you get the idea, which is a check if the server has responded every 100 ms. Of course you have to set serverResponseReceived = true yourself in the onSuccess() function. Call the timer right after the RPC.



回答3:

GWT calls XMLHttpRequest.open() whith true as its third parameter which means the call will be asynchronous. I solved a similar need for testing purposes just forcing this third parameter to be always false:

private static native void fakeXMLHttpRequestOpen() /*-{
   var proxied = $wnd.XMLHttpRequest.prototype.open;

   (function() {
       $wnd.XMLHttpRequest.prototype.open =
           function() {
                arguments[2] = false;
                return proxied.apply(this, [].slice.call(arguments));
            };
        })();
}-*/;

After invoking fakeXMLHttpRequestOpen(), any further use of XMLHttpRequest will act synchronously. For instance:

remoteSvc.getResult(new AsyncCallback<String>() {
    @Override
    public void onSuccess(String result) {
        GWT.log("Service called!");
    }

    @Override
    public void onFailure(Throwable caught) {
        GWT.log("Service failed...");
    }
}

GWT.log("Last message");

will allways render:

Service called!
Last message

See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/open for XMLHttpRequest.open() specification.