views:

1051

answers:

1

I'm trying to do something exceedingly simple: write a function that reads text from a text file and returns the text in a string using AS3.

The Function

    public function readData(path:String):String
 {
  var dataSet:String;

  var urlRequest:URLRequest = new URLRequest(path);
  var urlLoader:URLLoader = new URLLoader();
  urlLoader.dataFormat = URLLoaderDataFormat.TEXT;
  urlLoader.addEventListener(Event.COMPLETE, urlLoader_complete);
  urlLoader.load(urlRequest);

  function urlLoader_complete(evt:Event):void {
   dataSet = urlLoader.data;
   trace(dataSet)
  }
  trace(dataSet);
  return dataSet;
 }

Calling the Function

    var dataString:String = aq.readData("http://example.com/data.txt");
    trace(dataString);

This code returns a null string when I run it. Why?

EDIT: Ok, I now see that this doesn't work because urlLoader is acting asynchronously. I'm writing a program that reads in a data file and acts on it. Does this mean that I need to write the rest of my program inside function urlLoader_complete? Or should I pause the program until urlLoader is finished?

+2  A: 

In Flash and Flex, all network I/O is asynchronous. It has to be this way in order to avoid blocking your browser.

As a result, it is not possible to write a readData() function that directly returns the result of a network read operation. You will have to pass a callback function to the readData() function. When readData() has finished reading the data, it can call the callback function.

For example:

/**
 * Asynchronous function to read data as a string. When the data has been read,
 * the callback function is called with the result.
 * 
 * @param path     the URL to read
 * @param callback the function that is called with the result; should take
 *                 one string argument.
 */
public function readData(path:String, callback:Function):void
{
    var dataSet:String;

    var urlRequest:URLRequest = new URLRequest(path);
    var urlLoader:URLLoader = new URLLoader();
    urlLoader.dataFormat = URLLoaderDataFormat.TEXT;
    urlLoader.addEventListener(Event.COMPLETE, urlLoader_complete);
    urlLoader.load(urlRequest);

    function urlLoader_complete(evt:Event):void {
        dataSet = urlLoader.data;
        trace(dataSet);
        callback(dataSet);
    }
}

Here is how you might call that function from Flex:

<mx:Label id="mylabel" />
<mx:Button click="readData('http://www.google.com/',
    function(s:String):void {mylabel.text = s})" />
Mike Morearty
Hey Mike, turns out your suggestion didn't give me what I needed because I'm looking to create a sequence of actions. Take a look at my edits above.
msutherl
This is one of the trickiest things about ActionScript programming (and JavaScript programming, for that matter, which has the same issue): Some things are asynchronous, and there is really nothing you can do to prevent that. The only thing you can do is adjust to that async model.In my sample, you would write the next step of your program inside the callback() function.If you want to indicate to the user that he should wait, you could change the cursor (google "flex wait cursor") or draw a spinner or display the word "Loading..." or something.
Mike Morearty
I supposed I could do a: while(dataSet == null) { if(dataSet != null) { break; } else { trace("loading data") } }?
msutherl
You can't do a while (dataSet == null) loop because that might cause the application to freeze or even cause the function to time out if it takes a long time to load the data. Also, I'm pretty sure that the actual data loading is not running in a separate thread, meaning that the data would not be able to load while your function is still executing.
Cameron
Yeah, I found that out the hard way. Now I have a cascade of functions that trigger each other through even listeners. Surely there must be a better way to do this (though perhaps not with ActionScript).
msutherl