[Solved] How to get value out of arrow function?

Hello,

I am trying to extract a value out of a arrow function, which doesn’t work. I am using the URLLoader Class from OpenFL if this is relevant. Code example:

public function testDbCon():String {
var localResult:String ="empty";

urlLoader.addEventListener(Event.COMPLETE, (e) -> {
     var local = cast(e.target, URLLoader);
     localResult = local.data;
     trace(localResult); //trace 1
});
trace(localResult); //trace 2
return localResult;

}

Inside the arrow function the variable localResult has the correct value (a JSON String), which is shown/confirmed in trace 1, too.

But in trace 2 the value is still the string “empty”. I’ve tried several things, like using another function as a “helper” to extract the value from the arrow function and so on. Currently I have no ideas anymore how to get the wanted value out of the arrow function.

How can I solve this?

Hello,

localResult is invalidated after the function return, therefore addEventListener (closure) function must create localResult variable on the heap.

Try to make localResult as static variable and see what you will get.

Edit: trace 2 will always print “empty” because it is called upon listener creation, you have to trace (class) localresult after listener is triggered.

Well, I tried to make it a static variable. Unfortunately, this suggestion isn’t improving anything. I still can’t get the value outside of the arrow function.

Thats why I have problems to check, if the event was triggered, too. Because I can’t set any flags from inside the arrow function.

Is static variable inside the class? Mind to share code?

You can try to make property variable and then print message in setter to know if listener is triggered.

Here is the complete class, already with property variable, which isn’t working, too:

package controllers;

import openfl.net.URLLoader;
import openfl.net.URLRequestMethod;
import openfl.net.URLRequest;
import openfl.events.Event;

class CouchDBInterface {
    private var req:URLRequest;
    private var urlLoader:URLLoader;
    private var urlData:URLLoader;
    private var urlPrefix:String = "http://";
    private var urlPrefixSecure:String = "https://";
    private var couchDbServerIP:String;
    public var result:String; 
    static var localResult:String = "void";

    private var trig(default, set):Bool;

    function set_trig(newTrig) {
        return trig = newTrig;
    }

    public function new(cdbServer:String, secureConnection:Bool){

        couchDbServerIP = cdbServer;

        if (secureConnection){
            req = new URLRequest(urlPrefixSecure + couchDbServerIP);
        } else {
            req = new URLRequest(urlPrefix + couchDbServerIP);
        }

        req.method = URLRequestMethod.GET;
        
        urlLoader = new URLLoader(req);    
    }

    public function testCouchDbConnection():String {

        set_trig(false);
       
        urlLoader.addEventListener(Event.COMPLETE, (e) -> {
            var local = cast(e.target, URLLoader);
            localResult = local.data; 
            set_trig(true);  
            trace("It is: " + local.data);
            //arrowFunctionHelper(local);
        });  
        trace("Status var trig: " + trig);
        return localResult;
    }

    private function arrowFunctionHelper(u:URLLoader):Void { //This isn't working either
            this.result = u.data;
            trace("helper result" + this.result);
    }
}

Well, URL Request/Loader doesn’t work, have no clue why. However, I managed to get http query working

    /* This goes to constructor */
	httpQuery = new sys.Http(...);
	httpQuery.onError = onError; // error handler
	httpQuery.onData = (s:String) -> { set_trig(true); trace(s); } 
    /* This goes to testDBConnnetion */                                                                                                 
	httpQuery.request(false);                                                                                                                                        
    /* Add trace to property */
    function set_trig(newTrig) {
	    trace(newTrig);
        return trig = newTrig;
    }

Handler triggers property as expected.

I don’t have an exact solution to offer you, but I’d like to clear up one misconception you seem to have. When you add an event listener, it doesn’t wait to run more code in the current function until the listener is called. It continues running code in the current function, and the listener will be called at some point in the future. With this in mind, localResult returned by testDbCon() must always be “empty”. The URLLoader won’t complete loading until later, after testDbCon() returns.

You need some way to handle this “asynchronous” result. You could potentially pass in some kind of function to be called when the result is complete. Or you could dispatch your own custom event that you can listen to before calling testDbCon().

Thank you for confirming that you can reproduce this behaviour of URLLoader. I was really surprised and already questioning myself.

However, I will use another solution than URLLoader. Because this Project is an OpenFL one, I thought it would be more “streamlined” to use OpenFL packages for this.

Thank you for your clarification.

I understand how event listeners are working. Its a little bit unlucky that in this example a running condition and problems to get values out of this arrow function are mixed up.

And yes, this is of course a asyncronous situation. But because I can’t get any value out of this arrow function in combination with URLLoader, I can’t implement any functionality to take care of this.

I tried to pass in some kind of function. See the bigger code snippet. And I still couldn’t get the value out of the URLLoader instance.

That’s not what I meant. You’re just calling another function in your class, and it doesn’t affect the result of testCouchDbConnection() because it still happens asynchronously. I meant something more like this:

public function testCouchDbConnection(callback:(String) -> Void):Void

Then, when the data completes loading, call the passed in function:

callback(local.data);

Notice that the return value of testCouchDbConnection() is now Void. This is because it simply cannot return anything from the URLLoader. Calling testCouchDbConnection() is synchronous, but the response from the URLLoader is asynchronous. Instead, you need some way to “return” the result later. A common way to do that is callback function. As I mentioned, dispatching a custom event is another way.

Obviously, your code that calls testCouchDbConnection() will also need some modification because testCouchDbConnection() can’t return anything. It needs to wait until the callback is called before it continues to whatever it needs to do next. This could be inside the callback itself, or the callback could call another function.

Well, callback was the right solution and is an elegant approach, too. I would have tried to reinvent something like callback.

Thank you very much for your patience with me! :smile:

For the reason why I don’t want to use a custom event listener in this particular case, I have to explain my background a little bit. Years ago, my “true” entry into developing software was Flash with Action Script. I did this for some years, but had to take a break for some years as well. So far, it feels a little bit like homecomming.

However, what I learned there in the past, was that with the extensive usage of events for everything, event bubbling will become very quickly nondeterministic. Thats why I try to limit events mainly for input and “slow” stuff when there is no practicable alternative.

1 Like

I was just throwing custom events out there as an alternative. A callback is perfectly fine too.

Aside: When I create custom events, they almost never bubble. I find that people tend to overuse bubbling with custom events. It may be convenient to listen for an event indirectly from the parent, grandparent, etc., but it usually doesn’t make sense conceptually. With a MouseEvent, for instance, you may click the deepest display object in the tree, but you can also say that the parent, grandparent were also clicked. Most events, in my experience, make sense only in the context where they were originally dispatched, and they don’t necessarily apply to the dispatcher’s parent or anything further up the tree.

1 Like