Matching web service results to requests in Flex

2019-04-11 13:22发布

问题:

A little (!) bit of background before I can get to the question :

I have an accordion control loaded with an array of grids, each of which is lazy loaded with arrays of things. I'm using an auto-generated web service proxy to retrieve these lists. I'd like for the user to be able to change the selected child in the accordion without having to wait for the web service to respond. I was originally using the same proxy instance for all requests and keeping track of the requests in the order they were made, but the problem with this is that shorter arrays return more quickly from the server, so the order the requests were made in becomes irrelevant.

I couldn't find an obvious way to determine the original request when handling a proxy result event so what I ended up with is a function which handles the change event on the accordion, instantiates a new webservice proxy, shoves that into a hashtable with the index of the selected child and then adds a closure as an event handler. i.e. something a bit like this :

private proxyTable:Object = new Object();
private function PopulateThingGrid(index:Number):void
{
    var grid:ThingGrid = myAccordion.getChildAt(index) as ThingGrid;
    grid.things = ArrayCollection(proxyTable[index].getThings_lastResult);
}

private function SendThingRequest(index:int):void
{
    var grid:ThingGrid= myAccordion.getChildAt(index) as ThingGrid;
    if (grid.things.length == 0)
    {
        if (proxyTable[index] == null)
        {
            proxyTable[index] = new MyWebServiceProxy();
        }
        var proxy:MyWebServiceProxy= proxyTable[index];
        proxy.addgetThingsEventListener(function ():void { PopulateThingGrid(index); });

        var list:ThingList = thingLists.getItemAt(index) as ThingList;
        proxy.getThings("thinglist", list.ListID);
    }
}

private function myAccordion_Change(event:IndexChangedEvent):void
{
    SendThingRequest(event.newIndex);
}

(I've tried to anonymise this a bit, so I may have missed something, but hopefully you get the idea)

So, to the question(s) : is there an easier way to match up proxy results with the original requests that I'm just missing?

If not, is what I've done reasonable? I'm a little concerned about the number of proxy instances that I could end up generating and then disposing of them correctly (when that becomes necessary) - are there any pitfalls I might not be aware of?

Update : I think the problem may arise because the generated proxy code subclasses the ResultEvents from flash.events.Event, rather than mx.rpc.events.ResultEvent. I'm not entirely sure why it does this - the only way to access the AsyncToken is when it's initially returned by the method call.

回答1:

Not sure if this helps, but I had a similar situation, where I had a RemoteObject on which I call the 4 CRUD methods, but only one resultHandler. I solved this using the AsyncToken.

My RemoteObject calls looked like this:

public function list() {
    var token:AsyncToken = myService.list();
    token.operation = "list";
}

public function update() {
    var token:AsyncToken = myService.update(selectedItem);
    token.operation = "update";
}

public function create() {
    var token:AsyncToken = myService.create(selectedItem);
    token.operation = "create";
}

public function delete() {
    var token:AsyncToken = myService.delete(selectedItem);
    token.operation = "delete";
}

Then, the resultHandler looks like this:

public function resultHandler(event:ResultEvent):void {
    if( event.token.operation == "list" ) {
      // do something
    }   else if( event.token.operation == "update" ) {
    // do something
    }   
    // etc...

operation is a dynamic property, not a member of AsyncToken, so you can call it whatever you like. There's a Cookbook article describing it here:



回答2:

Ok, I think I understand: since the requests are made at selection-time, and you're calling through a local proxy, and the responses come back at random, you can't tell which response maps to which UI element. Makes sense.

Hmm. It's sort of hard to say without knowing what kind of "thing" your proxy object is, but assuming it's something concrete that maps appropriately to a UI object -- say, ListOfWidgets to a DataGrid -- then it's probably better to have each grid map to its own instance of a ListOfWidgets proxy, anyway. In that case, when you instantiate a proxy, you can pass it the index of the grid its results should populate (similar to what you're doing above, but with the intention of storing the index as a public member on the proxy object, as opposed to in a separate object). Then, since each proxy's result-event handler should provide you with access to the proxy through the target property of the event, you can retrieve the proxy's target index (e.g., event.target.index) and populate the appropriate grid.

public class MyProxy
{
    public var targetIndex:uint;

    public function MyProxy()
    {
        // ...
    }
}

private function handleSelection():void
{
    var proxy:MyProxy = new MyProxy();
    proxy.addGetThingsEventListener(Event.YOUR_RESULT_EVENT, yourHandler); 
    proxy.targetIndex = 1;
}

private function yourHandler(event:Event):void
{
    fillGridWithResults(event.target.targetIndex);
}

The coding model is a little simpler if you were using data binding, in which case you could do something more like this, provided either your proxy class or its results collection were marked Bindable:

<mx:DataGrid dataProvider="{proxy.results}" />

... and have things "just work" (since each proxy's instance would be unique). Does this make sense? Hopefully I've understood the problem correctly. ;)



回答3:

I am afraid I don't fully understand all of what you are trying to do, but If I am understanding correctly, you are making many requests and getting multiple responses back asynchronously and you are trying to match up the response with the request.

I have done this in the past for a similar problem, I was calling a webservice on keyup in a text field to do a "search as you type" sort of feature, but what that meant was that I always only cared about the response for the last request. So I changed the service to also take a parameter of a timestamp (down to the millisecond) and return it back with the rest of the response. Then I could either look for the latest timestamp in the responses or on the flex side, keep track of the timestamp of the last request and look for that specific response.

You could also do this with any kind of UUID or anything unique. It takes a little more work on the service side, but it scales well and is easily understood.

The other thing you could possibly look at, and I apologize for not researching this more myself is to look in debug at the request and response objects that are generated. You might find some ancillary information with those objects that you could look at to do the matching. But I am afraid it will depend greatly on exactly how you are calling and receiving the response.

HTH and hope I have understood the problem well enough.