I'm querying Rotten Tomatoes' API for movies listed in an XML document. The problem I'm having is, whist iterating through and querying for each movie I'm hitting RT's API limit which, in turn, is throwing an IO error.
Is there a way I can delay execution in a loop for about a second or so, to avoid this?
Short answer: use a Timer.
Suppose you have 100 movies in an array:
var movies:Array;
And for each of these movies you want to call an API on the server. First I would maintain a variable to mark the current movie:
var currentMovieIndex:int = 0; // first movie
Then, assuming you're using URLLoader:
private function makeAPICall():void
{
// Construct URLRequest based on current movie object.
var urlRequest:URLRequest = getURLRequest(movies[currentMovieIndex]);
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener("complete", completeHandler);
urlLoader.load(urlRequest);
}
getURLRequest() being a function that gives you a URLRequest object for the movie you pass to it as an argument.
In your completeHandler():
private function completeHandler(event:Event):void
{
...
if (++currentMovieIndex < 100) {
var timer:Timer = new Timer(3000, 1); // 3-second delay
timer.addEventListener("timer", timerHandler);
timer.start();
}
}
In your timerHandler():
private function timerHandler(event:Event):void
{
makeAPICall();
}
What are you loading from the xml that is causing the error? XML should not take long to load. If your loading items contained in the xml then a timer will not be helpful. It'll take too long and give a bad experience. Say the user has a fast connection but still has to wait on a delay for each file. What I'd do is check to make sure I've loaded one file(loadBytes) before moving to the next file. You'd only have to do this with the largest file contained in each node should be enough.
That would be more flexibly dependent on the users connection.
Related
I'm really not familiar with Action Script 3 at all but I am with other languages.
I'm hoping someone could help me.
I'm attempting to make a modification to JWplayer so that an rtmp stream is retrieved via a PHP script rather than it being supplied in the HTML.
The code I currently have is below:
function useData(event:Event):void {
var data:String = event.target.data.toString();
}
/** Load content. **/
override public function load(itm:PlaylistItem):void {
_item = itm;
_position = 0;
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, useData);
loader.load(new URLRequest("http://192.168.0.12/phpauth/play1.php"));
// Set Video or StageVideo
if(!_video) {
_video = new Video(320, 240);
_video.smoothing = true;
_video.addEventListener('renderState', renderHandler);
// Use stageVideo when available
if (_stageEnabled && RootReference.stage['stageVideos'].length > 0) {
_stage = RootReference.stage['stageVideos'][0];
_stage.viewPort = new Rectangle(0,0,320,240);
_stage.addEventListener('renderState', renderHandler);
}
attachNetStream(_stream);
}
// Load either file, streamer or manifest
if (_item.file.substr(0,4) == 'rtmp') {
// Split application and stream
var definst:Number = _item.file.indexOf('_definst_');
In the load function the file name to play is held in _item.file. I'm trying to make a call to a php script which then overwrites the value in _item.file. I've confirmed that the php is being called but I don't know how to get the data from the data string in the useData function into the _item.file string.
Any help would be really appreciated - I suspect this is a simple one but my lack of AS3 knowledge is making it really difficult.
Thanks,
Your problem basically about how to access a local variable in an event handler. A quick and dirty way can be to have an anonymous function used as a handler like:
loader.addEventListener(Event.COMPLETE, function(event:Event):void {
var data:String = event.target.data.toString();
_item.file = data;
});
This approach would work, because this anonymous function has access to the local variables inside load function as is. But, you need to be cautious that the anonymous function uses the variable exactly as the calling function is using. So, let's say there is a loop in load function and _item changes in every iteration of the loop. For that scenario, when load handler gets called, its _item would also have changed to the object which was last assigned to _item.
A far cleaner and OO approach can be to have a handler class like:
package {
public class LoadHandler {
private var _item:PlaylistItem;
public function LoadHandler(item:PlaylistItem) {
_item = item;
}
public function loadHandler(event:Event):void {
var data:String = event.target.data.toString();
_item.file = data;
}
}
and then have loader.addEventListener(Event.COMPLETE, (new LoadHandler(_item)).loadHandler). Hope that helps. BTW, LoadHandler could be made more generic to take and array of objects to be used and a callback function. loadHandler function, then could just call callback function with that array of objects.
If you are returning a simple string from PHP you should be able to use
event.target.data;
e.g. from PHP... echo "hello";
var data:String = event.target.data
You could try tracing the response to ensure you are getting something back from PHP.
You can either test this from within the IDE or install the Debug version of the Flash Player browser plugin.
trace("Response from PHP: "+event.target.data);
_item.file = event.target.data;
trace("_item.file: "+_item.file);
I am currently coding a game and I am storing all external files in a zip file to save space.
I am trying to use Fzip to load an image out of the Zip file and display it on stage but I am having no luck.
FZip Site - http://codeazur.com.br/lab/fzip/
Example 1 Site - http://www.tuicool.com/articles/7VfQr2
I have got the zip file to load and return me the number of files inside it but I cannot figure out how to retrieve an image called "test.png".
import deng.fzip.FZip;
import deng.fzip.FZipFile;
import deng.fzip.FZipLibrary;
import flash.display.*;
var zip:FZip = new FZip();
var loaderzippy:Loader = new Loader();
function unzipObb():void {
zip.load(new URLRequest("file://"+File.applicationStorageDirectory.nativePath+"/cake2.zip"));
zip.addEventListener(Event.OPEN, onOpen);
zip.addEventListener(Event.COMPLETE, dosomething); }
function dosomething(evt:Event):void {
trace("dosomething");
var img_file:FZipFile = zip.getFileByName("test.png");
var loaderzip:Loader = new Loader();
loaderzip.loadBytes(img_file.content);
var image:Bitmap = Bitmap(loaderzip.content);
addChild(image); }
I just get returned #2007: Parameter child must be non-null.
Any help would be great. Thanks.
When you load the PNG bytes using loaderzip.loadBytes(), you need an event listener to check when it's done loading. The documentation itself states:
The loadBytes() method is asynchronous. You must wait for the "init" event before accessing the properties of a loaded object.
Because you're trying to access the code directly after you called loadBytes(), at which point it hasn't actually loaded anything, you're getting a null bitmap.
Although you can access the data once the "init" event has been dispatched, it's best to wait for the "complete" event, since you want your image to be done loading (rather than halfway done).
I've tweaked your code to something that should work:
function dosomething(evt:Event):void {
trace("dosomething");
var img_file:FZipFile = zip.getFileByName("test.png");
var loaderzip:Loader = new Loader();
loaderzip.contentLoaderInfo.addEventListener(Event.COMPLETE, getImage);
loaderzip.loadBytes(img_file.content);
}
function getImage(e:Event):void {
var loaderInfo:LoaderInfo = e.currentTarget as LoaderInfo;
var image:Bitmap = Bitmap(loaderInfo.content);
addChild(image);
}
(I haven't tested this myself but it's just to give you the general idea anyway.)
Note that you should add the event listener to loaderzip.contentLoaderInfo, not just loaderzip. That's what actually dispatches the event and it also contains the loaded content.
I am writing simple metronome component using Flex + AS3. I want it playing 'tick1' sound after, for example, each 500 millisecond and each 4th time plays another sound 'tick2'. But actually delay between sound is not equivalent - sometimes lesser, sometimes greater a bit. I testing it on latest Chrome.
Here my code:
//Somewhere here button bound to the 'toggle' function
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.media.SoundTransform;
import flash.media.SoundChannel;
private var bpm:Number = 120; //2 bit per second, delay=500ms
private var period:Number = 4;
private var timer:Timer = new Timer(bpm, period);
[Embed(source='sounds/1.mp3')]
private var tickSound1Class:Class;
private var tickSound1:Sound;
[Embed(source='sounds/2.mp3')]
private var tickSound2Class:Class;
private var tickSound2:Sound;
private var trans:SoundTransform = new SoundTransform(1);
private function init():void {
....
tickSound1 = new tickSound1Class() as Sound;
tickSound2 = new tickSound2Class() as Sound;
update();
timer.addEventListener(TimerEvent.TIMER, onTimerEvent);
....
}
private function update():void {
timer.delay = 1000 * 60/bpm;
timer.repeatCount = 0;
}
private function toggle():void {
if (timer.running) {
timer.reset();
startStopButton.label = "Start";
} else {
update();
timer.start();
startStopButton.label = "Stop";
}
}
private function onTimerEvent(event:TimerEvent):void {
var t:Timer = event.currentTarget as Timer;
if (t.currentCount % period == 0)
tickSound1.play(0, 0, trans);
else
tickSound2.play(0, 0, trans);
}
I think there are two main reasons:
It is known that Timer object in Flash Player is inaccurate, delay between it's fires fluctuates.
Sound.play() method also introduces some delay before the sound actually starts to play, and theoretically this delay can fluctuate. The delay is especially noticeable in PPAPI version of Flash Player being used in Chrome.
There are several solutions. I would suggest one of these:
Use pre-composed sound of the whole metronome cycle (tick1-pause1-tick2-pause2) and just loop it using the second argument of Sound.play() method;
use dynamic sound generation.
The second option is more flexible but is more difficult to implement. Basicaly, you'll need to create a new instance of Sound object, subscribe to it's SAMPLE_DATA event and call it's play() method. In the handler you'll check event.position / 44.1, which will give you current position of sound generation in ms. Then, if you decide that it's time to play tick1 or tick2 sound, you'll call tickN.extract(event.data, ...), where tickN is tick1 or tick2 Sound object, or write the silence otherwise.
You can read more about dynamic sound generation here.
Also, notice that when you call Sound.play() it returns a SoundChannel object, which has the position property. Is it a position in ms of a sound that is being played (not generated) at the moment, and it is accurate. So, using this property, you can come up with the third approach: create a Sound object and setup a SAMPLE_DATA handler like in the dynamic sound generation solution, but write the silence (zeros) to the event.data object inside the handler all the time. This is needed to obtain a sound channel without actually playing a sound. Then, use high frame rate (60 FPS) and a Timer with the smallest possible delay (1 ms). Each time the Timer fires, check soundChannel.position to determine whether it is time to play the tick sound, and, if it so, just play it like you're doing in your example. This approach is likely to solve the problem of the Timer inaccuracy, but it cannot deal with the delay caused by tickSound.play() method.
I is to possible to regulate how often flash triggers SAMPLE_DATA event when sampling data from input device ? I need to sample constantly (even silence).
var mySound:Sound = new Sound();
mySound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
AFAIK currently this function gets called 20 times/s
playing with the sample code from http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/events/SampleDataEvent.html
var mySound:Sound = new Sound();
function sineWaveGenerator(event:SampleDataEvent):void {
for ( var c:int=0; c<8192; c++ ) {
event.data.writeFloat(Math.sin((Number(c+event.position)/Math.PI/2))*0.25);
event.data.writeFloat(Math.sin((Number(c+event.position)/Math.PI/2))*0.25);
}
}
mySound.addEventListener(SampleDataEvent.SAMPLE_DATA,sineWaveGenerator);
mySound.play();
the less data you write, the more often the method gets called.
if you call it too often the sound may get clicky and gross. try adjusting the frame rate.
Can't seem to get the value of myXML outside the function, despite being declared outside. What am I missing here? The data loads and traces correctly inside the function.
var myLoader:URLLoader = new URLLoader();
myLoader.load(new URLRequest("flightPlannerBoard.xml"));
var myXML:XML;
// Check XML data fully loaded
myLoader.addEventListener(Event.COMPLETE, processXML);
function processXML(e:Event):void {
myXML = new XML(e.target.data);
//trace(myXML);
}
trace(myXML);
Because ActionScript is asyncronous as others have said, you cannot control the flow of execution by code placement. What you must do is control execution through the events, and so whatever actions you want to perform with the loaded XML should be in the processXML function or in another function that is called from processXML:
var myXML:XML;
function processXML(e:Event):void {
myXML = new XML(e.target.data);
trace(myXML); //this trace will work
doNextAction();
}
function doNextAction():void {
trace(myXML); //this trace will also work
}
You should declare your XML variable outside your function in order to be able to use it in another function
private var myXML:XML;
Actionscript is an asynchronous language, meaning the trace "outside" the callback will be called before the file has loaded. The execution order in your case is:
create instance of URLLoader
start loading file
add event listener to listen to the complete event
trace out myXML
(or at some point later) finish loading xml file