Jump to content
GreenSock

eigenface

LoaderMax onComplete not getting called [SOLVED]

Recommended Posts

Here is a method I'm calling multiple times in rapid succession:

 

public function loadWords(url:String):void
{
var loader:DataLoader = new DataLoader(url, { format:URLLoaderDataFormat.TEXT, onComplete:wordsCompleteHandler, onFail:failHandler, autoDispose:true });
loader.load();

if (parentLoader == null) {

	parentLoader = new LoaderMax({ onComplete:allWordsCompleteHandler, autoDispose:true });
}
parentLoader.append(loader);
}

parentLoader is an instance var for the class instance this method belongs to, and starts out null. wordsCompleteHandler ends up getting called once for each time this method is called (good), failHandler never gets called (good), but neither does onComplete:allWordsCompleteHandler (bad). Shouldn't allWordsCompleteHandler get called once all the DataLoaders appended to parentLoader complete? Is the order I'm appending/loading/instantiating f*cking things up somehow? Seems like the above code should work fine.

 

I tried changing the method to this:

 

public function loadWords(url:String):void
{
if (parentLoader == null) {

	parentLoader = new LoaderMax({ onComplete:allWordsCompleteHandler, autoDispose:true });
}
var loader:DataLoader = new DataLoader(url, { format:URLLoaderDataFormat.TEXT, onComplete:wordsCompleteHandler, onFail:failHandler, autoDispose:true });

parentLoader.append(loader);
}

That is, I made sure the parentLoader was instantiated before any DataLoaders were instantiated, and I didn't load any of them individually. Instead, I called parentLoader.load() after the last time loadWords was called. This produces the following error:

 

TypeError: Error #1009: Cannot access a property or method of a null object reference.
at com.greensock.loading::LoaderMax/_loadNext()
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at com.greensock.loading.core::LoaderCore/_completeHandler()
at com.greensock.loading::DataLoader/_receiveDataHandler()
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at flash.net::URLLoader/onComplete()

This looks like LoaderMax getting confused about what it should load next. Not sure what's going on.

Link to comment
Share on other sites

I updated all greensock classes today, immediately before trying the above.

Link to comment
Share on other sites

In the first example, the LoaderMax never fired a COMPLETE event because you never called load() on it. In other words, it wasn't active. Technically its children were being loaded independently, but a LoaderMax should not "complete" if it was never directly asked to take an action - see what I mean? The solution would be to load() the LoaderMax, but it sounds like you tried that in your second example but you got an error. I wasn't able to replicate any error like that, so I'm very curious to find out how you did it. Can you post an example? I tried copying your code and adjusting it so it's loading files I have, but I couldn't get it to fail. Worked like a charm every time.

 

Can you show me how to get that error? Again, a sample FLA (with support files) would be AMAZINGLY helpful.

Link to comment
Share on other sites

Ok, I'll pull out a "clean" example.

 

In the meantime, I see what you mean about LoaderMax not firing an event if it was never directly asked to take an action, but is that really the behavior you want? It seems to me, if it's ever the case that all of a LoaderMax's children complete loading, the LoaderMax onComplete should fire. It just doesn't seem logical that all the children can complete without the parent also being "complete", see what I mean? Is there something I'm missing?

Link to comment
Share on other sites

It'd be like saying, "I put turkey, stuffing, and yams on my TODO list, and then I finished cooking the turkey, the stuffing, and the yams, but I'm not officially done with the TODO list (even though turkey, stuffing, and yams are the only things on it), because I never officially picked up the TODO list and said 'Okay, I'm starting now.'" Does that make sense?

Link to comment
Share on other sites

I do see what you're saying and a case could be made for either way being more "logical" or "intuitive". I certainly thought about this when building LoaderMax and wrestled with it a bit. But by your logic, a LoaderMax's "COMPLETE" event would fire in all of the following scenarios (which seemed somewhat illogical to me):

 

1) If a LoaderMax has several completed loaders but one that isn't and then I dispose() or remove that child that hasn't loaded. Disposing a child could cause its parent to complete even though the parent wasn't actively loading anything.

 

2) If a LoaderMax has a status of PAUSED with all of its children loaded except one, and then I dispose() or remove that child. That LoaderMax has now COMPLETED according to your logic even though its status is PAUSED.

 

3) If I append() a loader to a completed LoaderMax and then dispose() or remove it.

 

4) If a LoaderMax is empty and then I append() a loader that has completed, the LoaderMax would need to dispatch its COMPLETE event even though no network activity occurred and neither the child's nor the LoaderMax's load() method was called at that point (well, the child's must have been at some previous time, but you get the point). Then, if I immediately append() another loader that hasn't loaded yet, the LoaderMax's status would change back to READY and then if I remove/dispose it and append() another loader that has finished loading, the LoaderMax would have to dispatch its COMPLETE event again. Multiple COMPLETE dispatches with no loading. Weird?

 

So again, it seemed to me like COMPLETE indicates the completion of an action that you asked it to take, not so much the status of all the children. From what I recall, that's the standard behavior across all of Adobe's classes too. I guess what you're describing is more of a ALL_CHILDREN_COMPLETE event :) You could sense that by listening for CHILD_COMPLETE events and in that handler, check the LoaderMax's "progress" property - if it's 1 it has loaded all of the children.

Link to comment
Share on other sites

Yeah, that makes sense - Events are responses to specific requests, and the loader's state of completeness is represented by its progress property.

 

Although, it seems like there are also weird cases with the way LoaderMax currently works. For example, while in the process of loading one child, you append a second child, then the first finishes loading, and then you remove the second one. LoaderMax never fires a complete Event, right? Is there a relatively simple way to state under what circumstances the complete event fires?

 

I think the error I posted is an event listener priority issue - according to my trace statements, allWordsCompleteHandler was firing before the final wordsCompleteHandler. And when I don't use autoDispose, I don't get the error. I think allWordsCompleteHandler was happening first, and disposing of the final DataLoader, which caused the error.

 

Aha! And apparently the reason allWordsCompleteHandler was happening first is because I'm calling dispose manually on each DataLoader as it completes, in wordsCompleteHandler. This seems like a real bug, because it happens whenever I manually call dispose in wordsCompleteHandler, regardless of whether or not I've set the DataLoaders to autoDispose - it's not a matter of disposing of the DataLoader twice (which shouldn't cause problems anyway.) In fact, I'm disposing of the LoaderMax twice (once with autoDispose and once manually inside allWordsCompleteHandler) and this doesn't seem to cause any problems. Here are my handlers.

 

protected function wordsCompleteHandler(event:LoaderEvent):void
{
trace("wordsCompleteHandler");

var loader:DataLoader = event.target as DataLoader;
wordsStr = loader.content;

loader.dispose(); // comment out this line to prevent Error
}
protected function allWordsCompleteHandler(event:LoaderEvent):void
{
trace("allWordsCompleteHandler");

if (parentLoader) {

	parentLoader.dispose();
	parentLoader = null;
}
}
protected function failHandler(event:LoaderEvent):void
{
if (parentLoader) {

	parentLoader.dispose();
	parentLoader = null;
}
trace("ListData, load failed, url: " + event.target.url);
}

Link to comment
Share on other sites

...Although, it seems like there are also weird cases with the way LoaderMax currently works. For example, while in the process of loading one child, you append a second child, then the first finishes loading, and then you remove the second one. LoaderMax never fires a complete Event, right?

Nope, I don't think that's correct. If you load() a LoaderMax, you'll ALWAYS get either a COMPLETE event or a FAIL event. Period. There are no exceptions (unless there's a bug that I'm unaware of). Well, okay, I take that back: the only exception is if you pause() the LoaderMax between the time load() is called and when it completes or fails (for obvious reasons).

 

As far as the error you were getting, I indeed found the issue and it should be fixed in version 1.763 (just posted). It would only happen in a very specific scenario (as you described) where you're calling dispose() multiple times on a child and the parent LoaderMax has autoDispose:true. But again, it should be fixed now. It wasn't an event listener priority issue - it just had to do with adding one condition in a statement to skip it if a certain variable was null. Thanks for letting me know about the problem.

 

For the record, there's no reason to manually call dispose() on your loader after it loads if you've already got autoDispose:true. It won't hurt anything (besides a very slight performance hit due to the extra function calls), but it doesn't help either.

 

Let me know if the latest version works well for you.

Link to comment
Share on other sites

Thanks a lot for the fix - this works as expected now.

 

So autoDispose disposes of a loader after the complete event fires (after complete event handlers are called.) But it doesn't dispose of the loader after fail events, right? So it will always complete or fail, but not always autoDispose. What was the rationale for that? I would have assumed it would always autoDispose.

 

I'm kinda confused by your response to my statement. In the example where in the process of loading one child, you append a second child, then the first finishes loading, and then you remove the second one (before it finishes loading, I meant), what happens? A complete event or a fail event?

Link to comment
Share on other sites

Thanks a lot for the fix - this works as expected now.

Super.

 

So autoDispose disposes of a loader after the complete event fires (after complete event handlers are called.) But it doesn't dispose of the loader after fail events, right? So it will always complete or fail, but not always autoDispose. What was the rationale for that? I would have assumed it would always autoDispose.

The reason autoDispose wouldn't dispose if/when the loader fails is because:

 

1) The loader didn't complete its job, so it doesn't seem like it should automatically be whisked away never to be seen again. Imagine you're the boss of a fleet of delivery trucks (loaders) that are sent out to do jobs. You instruct them "do ___ and then go home because you'll be done for the day" (the equivalent of autoDispose:true). The trucks that successfully do their jobs (pick up and deliver their goods) are dismissed but if one or more run into problems, they should stick around so that you can decide what you want them to do (if anything). autoDispose:true is kinda like saying "if you do your job, I don't need you anymore, so go away." That's part of the reason I called it "autoDispose" instead of "alwaysDispose" or something like that - it is supposed to imply that there's some intelligence behind the automation. It'll dispose when appropriate (when it does its job successfully).

 

2) It would prevent inspection of the failures. For example, if I put loaders into a LoaderMax and one (or more) fails, I'd want to be able to loop through the children and inspect the ones that had their status set to FAILED or count how many failed, etc. I want the failures held accountable rather than letting them skip out without doing their job :)

 

I'm kinda confused by your response to my statement. In the example where in the process of loading one child, you append a second child, then the first finishes loading, and then you remove the second one (before it finishes loading, I meant), what happens? A complete event or a fail event?

The only time a FAIL event would be dispatched is if one of the loaders literally fails to load. In your example, the LoaderMax would indeed dispatch a COMPLETE event because...well...it completed its task of loading its children (even if you juggle the children around on the fly, LoaderMax is smart enough to handle that). Is there something about that explanation or behavior that seems odd to you? (maybe I'm missing something obvious)

Link to comment
Share on other sites

No, that makes sense in light of the "make a request, get an event" idea. You told it to load, so it has to either complete or fail, and if you deliberately remove one of its children, I guess it didn't fail at what you intended it to do. I just hadn't fully wrapped my head around the idea.

 

So if the LoaderMax only has one child, and you remove that child while it's loading, does the LoaderMax complete or fail? By the above logic, it should complete, but then again it never actually completed loading anything.

Link to comment
Share on other sites

So if the LoaderMax only has one child, and you remove that child while it's loading, does the LoaderMax complete or fail? By the above logic, it should complete, but then again it never actually completed loading anything.

Yep, it'd dispatch a COMPLETE event because you asked it to load() and nothing failed and it reached the end of its queue. You wouldn't expect it to FAIL in that scenario, would you?

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×