Can I force actionscript code to be executed lineary?
Let's say I have some cod开发者_如何学运维e like this in AS3
for(...)
thing = new Thing(...)
The constructor for Thing is going to cause a swf to be rendered onto some movieclip. If the loop is run 10 times or so, most of the time I will only end up with a subset of Things showing up on the clip. I'm guessing this is a problem that flash is having loading a bunch of Things at the same time.
I've been able to solve this problem by passing callbacks to the Thing constructor, and only continuing the loop once the previous Thing is completely loaded and calls back.
First of all, is this a known problem? Or am I going about this in the wrong way? Secondly, is there any way I can tell flash/as to run this code in a linear fashion without the callbacks? It gets kind of annoying doing this everywhere.
EDIT:
So I think richardolsson is right, we must be doing something wrong. To restate the problem, if I try to load multiple swfs onto the stage, even as few as two or three, without waiting for the previous one to finish loading, I will get an unpredictalbe number loaded. For example, running the loop above 10 times, I may end up getting the 2nd, 5th and 10th Things loaded. The others seemingly getting lost in asynchronousity. Flash shows no runtime errors in these situations. So here's the basic idea of how we are doing things.
We have a base class that we extend for most classes that load swfs. It looks something like:
public function base(path:string, parent:sprite, callback:function)
{
//this is the child swf path
_path = path;
//parent is the sprtie we want to render the child onto
if(parent != null) doLoad(parent, callback)
}
public function doLoad(parent, callback)
{
var mLoader:Loader = new Loader();
var mRequest:URLRequest = new URLRequest(_path);
//onLoaded is overridden in the child. The override first calls the parent onLoad.
mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);
mLoader.load(mRequest);
}
//this is called from the load complete from above. This is done before the
//child onloaded is done
protected function onLoaded(loadEvent:Event):void
{
_loadedTarget = loadEvent.currentTarget;
_canvas = _loadedTarget.content;
_parentSprite.addChild(_canvas);
}
Now, most of our classes which need to show a Swf extend this class. They look like this
public class child extends base
{
public function child(parent:Sprite, callback)
{
//call base constructor
super("some/path/to/swf", parent, callback);
}
protected override function onLoaded(loadEvent:Event):void
{
super.onLoaded(loadEvent);
....
}
This is the basic scheme. If I try to create a bunch of instances of 'child' WITHOUT waiting for the onLoaded function to finish on the previous 'child', random instances simply won't show up. I wrote the above code very quickly, it is not compy/paste, so ignore simple errors. Are there any glaring reasons why the above method does not work?
Could it be because you're declaring your swf loader in local scope? It's most likely falling out of scope and being garbage collected before it has a chance to load.
It's not so much of a problem as it is a different way of thinking. Instead of thinking of your program in an imperative, procedural fashion (i.e., do this, do that, do another), think of your program as object oriented events. (i.e., the completion of X triggers an event and Y is something that notices the event.) I still fight with my brain from time to time over this.
Maybe event listeners are more of what you need? Your Thing
objects could fire off some event when they complete and other parts of your code could listen for those events.
I think it sounds odd that your Thing instances don't show up. While it is true that it is often necessary to adopt an asynchronous way of thinking when programming for Flash, the way you describe this particular case makes me think you are doing something wrong. Flash shouldn't have a problem loading a bunch of SWF files and putting them on stage, especially not as little as ten.
As long as you are using separate Loader instances, and add each of those loaders to stage (or their content once the Event.COMPLETE event fires), you should theoretically be able to load an infinite number of SWF files. The browser will likely have some restrictions on the number of connections that can exist at a time, and so will queue them up, but that's transparent to Flash Player, and even more so to your code.
If you edit your post with a more real-world example of your code, it might be easier to find the problem.
Cheers
I'm going to take a stab at this. My AS skills are a bit rusty though.
Your...
protected function onLoaded(loadEvent:Event):void
{
_loadedTarget = loadEvent.currentTarget;
_canvas = _loadedTarget.content;
_parentSprite.addChild(_canvas);
}
Can you...?
var _loadedTarget = loadEvent.currentTarget;
var _canvas = _loadedTarget.content;
To keep these in scope of the called onLoaded
I'm thinking without the var, it might attach those variables to the outer scope and overwriting them each time the onLoaded gets called.
Or...?
_parentSprite.addChild(loadEvent.currentTarget._canvas);
So you don't have to set any vars and eliminate any possible scoping issues altogether.
Or maybe...?
_loadedTarget[] = loadEvent.currentTarget;
_canvas[] = _loadedTarget[_loadedTarget.length-1].content;
_parentSprite.addChild(_canvas[_canvas[_canvas.length-1]);
What do you think?
There is a very easy way to sort the async downloads. Just give the Loader a name! :)
var pic2loadcount:int = ProjectspictureNames.length;
var ImageLoader:Loader;
for (var counter:int = 0; counter < pic2loadcount; counter++) {
ImageLoader = new Loader();
ImageLoader.name = String(counter);
ImageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE , onProjectImagesLoaded);
ImageLoader.load(new URLRequest(path2pics + ProjectspictureNames[counter]));
}
and finally in your onProjectImagesLoaded function, grab the name (onProjectImagesLoaded), convert is back to an int, and you are ready to sort! :)
Cheers, Dirk
I don't think you've given enough information to get a definitive answer.
It sounds like your "Thing" object has some sort of initialization to it that is asynchronous, and therefore the execution path is unpredictable because a given request could finish out of order.
You might not be able to literally manage the execution path, but you can take steps to manage the objects so that they at least appear to the user as if they executed in order.
Namely assigning an index to the given object or a reference back to an array containing the series of objects, and having them analyze each other to see which are completed and then run accordingly.
Ultimately that's pretty silly, you should consider an application design that doesn't simultaneously subsist on asynchronous events AND the appearance of linear execution.
But really, to give a definitive answer, you'd need to tell us more about your problem. There's often a simple solution if you look at it from the right angle.
Doesn't sound like it should be a problem, but on the other hand I've never triggerred 10 calls to load sw's at the same time.
In general I use some kind of loading queue, in which items are added (optionally with priorities to let them be sorted in order of importance) and are loaded when the previous item in the queue has finished loading.
Some general useful tips:
- use a webproxy to monitor what is exactly going on between your flash app and the server. I'm REALLY pleased with charles (http://www.charlesproxy.com/). It has a bunch of extra very useful features like throttling (faking slow D/L speeds) etc.
- Use traces to monitor what exactly goes on in terms of the 'flow' of your code. Many times you assume your code reaches a certain point, while in reality it does not. Put traces on key points in your code. Obviously it's a lot better to use a debugger. A very versatile (and extremely easy to learn how to use) debugger is MonsterDebugger (http://demonsterdebugger.com/) Actually it's a logger, but it'll help you tremendously anyhow.
精彩评论