AsyncToken behaviour with Socket- Flex - actionscript-3

I am using FlashSocket on my client side to make calls to socket.io on node.js server.
But i want specific values and function references to be available in the socket event listeners.
One approach that came to my mind was to pass the required values along with the request and make the server to send those parameters back along with the data. But that doesn't seem to help since I need the function handlers to be returned as well with the result.
The way i am doing it with http request is:
var token:AsyncToken = _service.send(item.params);
token.id = _tokenId;
token.params = item.params;
token.handler = item.resultHandler; // this would be called later in result or fault event handlers.
Is there a way to achieve this in flex sockets?
Thanks in advance.

You could use AsyncResponder class and anonymous functions.
var token:AsyncToken = _service.send(item.params);
token.addResponder(new AsyncResponder(
function(event:Object, token:Object = null):void {
// result handler, it has access to item.params, etc.
},
function(error:FaultEvent, token:Object = null):void {
// fault handler, it has access to item.params, etc.
}
));

Related

Create delay in ActionScript 3 function

I have a function in Adobe Flex 4 (ActionScript 3) that accepts an object and returns an ArrayCollection...
If a certain global variable is set to true, I want the function to delay itself for 3 seconds before running. Otherwise I want the function to run as normal.
The problem is, if I use a Timer, that timer calls a separate function, and that function cannot return anything to my calling function, nor can the function it calls accept any parameters, so it's not like I can call my own function recursively after the TimerComplete event fires... And a recursive call wouldn't work anyway, because it would return the ArrayCollection to the timer-result function, not to the original calling function...
I need a delay within the function, not a delay that causes me to go outside that function. But I cannot figure out how to do it.
Something like this is what I need to do:
private function createArrayCollection(myObject:Object):ArrayCollection {
var myArrayCollection:ArrayCollection = new ArrayCollection();
if (globalWaitBoolean) {
//delay here for 3 seconds, somehow
}
//Here I do the stuff that uses the info in myObject to figure out what to
//put into the ArrayCollection I want to return
return (myArrayCollection);
}
So... Any ideas on how to accomplish this without calling an external Timer function that cannot return an object back to my original function?
Thanks,
The way you want it you will have your whole application to lag for 3 seconds, unresponsive to any user input and external events. But it is possible, sure:
import flash.utils.getTimer;
private function createArrayCollection(myObject:Object):ArrayCollection
{
var myArrayCollection:ArrayCollection = new ArrayCollection;
if (globalWaitBoolean)
{
var waitUntil:int = getTimer() + 3000;
// Method getTimer() returns time in ms passed since app start.
// So you just have to wait until it is greater than appointed time.
while (getTimer() < waitUntil)
{
// Do nothing.
}
}
return (myArrayCollection);
}
Still, if you want to do it in a correct way of doing it:
import flash.utils.setTimeout;
private function callerMethod():void
{
// Blah blah blah.
// ...
// Finally.
createArrayCollection(sourceData, asyncResult);
}
private function createArrayCollection(myObject:Object, handler:Function):void
{
var result:ArrayCollection = new ArrayCollection;
if (globalWaitBoolean) setTimeout(handler, 3000, result);
else handler(result);
}
private function asyncResult(source:ArrayCollection):void
{
// The rest of your processing code.
}
Normal (synchronous) code flow would not return until the value is prepared, so should you desire to actually wait for 3 seconds while not allowing your app to do anything, use getTimer() approach from #Organis's answer. If you'll go for an asynchronus result, you'll need to face and overcome some more problems.
First, when do you expect your returned ArrayCollection to actually arrive. Speaking of code design, asynchronous code requires a whole lot of assumptions, thread safety etc etc, and even while AS3/Flash does not have true multithreading unless you count Workers, the code flow with events is not as obvious. So, whoever called your createArrayCollection() MUST NOT expect value returned from it right away. So, speaking about your direct question, NO, you can't avoid timers of some sort if you desire a responsive application. But you can use them with an approach that would involve an indirectly returned result.
Second, whether there might be concurring requests for more array collections from objects if your app would require these - you have to prepare for any kind of interference that might be caused by this. Say your function is triggered by a button click - what if that button would get clicked more than once in 3 seconds?
Third, actual route to processing code is not direct with asynchronous return. You need either a callback, an event handler (which is essentially a semi-native callback), a code that periodically checks for value presence (enter frame handler, etc) or a similar trick to gather the value that's returned asynchronously, and then transfer it to any relevant code that would process it further. Therefore, you would need to design an interface capable of receiving complex data (source object forward, array collection backward) and then carefully test it against all the possible cases and flaws.
An example of implementing all that is very long, I'll try to outline it somehow. Ler's assume you have a sort of "server" class that accepts requests for data and processes it synchronously (no wait) or asynchronously (wait). It accepts a source object of type "T" and provides a newly created object of type ArrayCollection, supplied as a parameter to whatever callback function sent to it. Also it accepts a delay (a simple way to show sync/async return would be a boolean, but why not getting an int?) as a parameter, and guarantees (to the extent of event model limitations) that after this delay the callback will be called ASAP. The architecture will then look like this:
class Processor {
Dictionary requests; // here all the requests that are delayed will be stored
public function dpr(source:T,callback:Function,delay:int=0):void{...}
// creates requests and stores them
private function syncProcess(source:T):ArrayCollection {...}
// whatever routine you want to get variably delayed
private function processTimeout(e:Event=null):void {...}
// processes events from "setTimeout()" and calls callbacks
}
Note that asynchronous approach forced to create three more entities than a synchronous one. First is the request holding structure (the dictionary here), second is timeout event handler, third is whatever callback you'll desire to get called when the data is ready. The code flow would go like this:
Synchronous call would result in the callback directly called from within the class: request->processTimeout->syncProcess()->callback. Asynchronous call will have the callback called from within Timer::timerComplete event handler via setTimeout called within request, with data that originally came from request stored in requests.
You could use an embedded/inline function:
private function createArrayCollection(myObject:Object):ArrayCollection {
var myArrayCollection:ArrayCollection = new ArrayCollection();
if (globalWaitBoolean) {
var milliseconds:int = 3000;
//delay here for 3 seconds
setTimeout(function()
{
//Here I do the stuff that uses the info in myObject to figure out what to
//put into the ArrayCollection I want to return
return (myArrayCollection);
},
milliseconds);
}
else
{
//Here I do the stuff that uses the info in myObject to figure out what to
//put into the ArrayCollection I want to return
return (myArrayCollection);
}
}
The inner function will have access to all local vars of the outer function.

AS3: Returning value from another function after event happened

Basically I want to check if a user exists in a database using AMF (and that works great!). But then I want to return the boolean value to another function (in another class) that originally called the "checkUserExistance" function. But, since the database connection isn't immidiate, this function will always return a false value (even if "result" is true). So I would like to have the return-line inside the "onUserChecked"-function but that of course gives me an error. I thought I could create an eventListener, but then, the "return userExists"-line would also have to be inside another function, which doesnät work(?)... What can I do?
public function checkUserExistance(username:String) {
var responderBOOLEAN:Responder = new Responder(onUserChecked, onFault)
var userExists:Boolean = false;
connection.connect(gateway);
connection.call("User.checkUser", responderBOOLEAN, username);
connection.close();
function onUserChecked(result:Boolean):void {
userExists = result;
}
return userExists;
}
I'm sorry but you are trying to force an Asynchronous call to a Synchronous one and this is WRONG.
See here
You should learn how to handle events in the correct way.
What can i suggest you that helped me a lot is this
The only true answer here is to save userExists as a member variable, and dispatch event when the server returns you a response. The client side of the things should be similar to:
// add listener, ServerEvent is a custom event (see below)
server.addEventListener(ServerEvent.CHECK_RESPONSE, onCheckResponse);
server.checkUserExistance('username'); // start the query
function onCheckResponse(e:ServerEvent):void {
if (e.userExists) {
}
}
// inside server class
function onUserChecked(result:Boolean):void {
userExists = true;
dispatchEvent(new ServerEvent(ServerEvent.CHECK_RESPONSE, userExists));
}
/* ServerEvent is a custom class that extens Event
Such classes are used so you can pass special properties in them
via constructor (pass data, store it into member variable)
and through getter for that variable.
If you don't like it, simply add/dispatch Event.COMPLETE
and use public property to get userExists from server
*/

closures with popups using flex 4.6

I have this custom event handler that shows a popup and accepts input from the user:
private var mySkinnablePopupContainer:MySkinnablePopupContainer;
private function handleShowGridPopupEvent(event:ShowGridPopupEvent):void {
var mouseDownOutSideHandler:Function = function(mdEvent:FlexMouseEvent):void {
// At this point, event.targetControl contains the wrong object (usually the previous targetControl)
if (mdEvent.relatedObject != event.targetControl) {
mySkinnablePopupContainer.close();
}
}
var gridPopupSelectionHandler:Function = function(popEvent:PopUpEvent):void {
if (!popEvent.commit) return;
// At this point, event.targetData contains the wrong object (usually the previous targetData)
myModel.doSomethingWithData(popEvent.data.selectedItem, event.targetData);
}
if (!mySkinnablePopupContainer) {
mySkinnablePopupContainer = new MySkinnablePopupContainer();
mySkinnablePopupContainer.addEventListener(PopUpEvent.CLOSE, gridPopupSelectionHandler);
mySkinnablePopupContainer.addEventListener(FlexMouseEvent.MOUSE_DOWN_OUTSIDE, mouseDownOutSideHandler);
}
// At this point, event.targetData contains the correct object
mySkinnablePopupContainer.dataProvider = getMyDPArrayCollection(event.targetData);
mySkinnablePopupContainer.open(this);
var point:Point = event.targetControl.localToGlobal(new Point());
mySkinnablePopupContainer.x = point.x + event.targetControl.width - mySkinnablePopupContainer.width;
mySkinnablePopupContainer.y = point.y + event.targetControl.height;
}
Every time the function handler gets called, it will have the correct ShowGridPopupEvent object but by the time it calls the
gridPopupSelectionHandler, it will contain the old object from a previous call. It works the first time, subsequent calls fails.
Somehow the reference to the event object changed somewhere in between before opening the popup and after.
Any idea what am I doing wrong here? Is this a bug with flex?
found the prob. since im attaching listener only once, it will reference the old listener, with the reference to the old data. i guess i was expecting its reference to be updated whenever i create the closure. not in this case. possible fix is to remove the listener and re-add it again but I abandoned the idea of using closures, and aside from what RIAStar mentioned, it is also impractical as it only gives more overhead by creating a new function for every invocation of the handler.

How to create Synchronous Function Flex

I have created a swf project with flex and i have some checkboxes on it and whenever the checkbox seleced it will call the checkbox handler:
function checkBoxHandler() {
//call resultHandling
var obj:myObjectType = new myObjectType();
resultHandling(obj)
}
function resultHandling(myObject:myObjectType) {
//implementation code to send a request to server side
}
is it possible to make the "resultHandling(...)" to be a synchronized function? So there will be a que whenever we make a call to that function especially when there are multiple function calls
As Flex is Flash the limitations to Flash apply to Flex too. One of these is (Actually in newer Flash versions this no longer is 100% valid) that Flash has only one thread. This thread does everything from updating/drawing the ui, processing the application logic, handling IO, etc. Therefore a synchronous call would be a blocking call and in Client-Server communication this block can be quite long. Therefore Flash doesn't support blocking calls to a server and you won't be able to find a solution for this ... unfortunately.
But be assured, actually you will start creating more robust applications this way. I have noticed creating more and more asynchronous solutions even in places I could use synchronous calls :-)
you can create an arraycollection and store all your myObjectType objects in it. Then do something like-
var arr:ArrayCollection = new ArrayCollection();
function checkBoxHandler() {
//call resultHandling
var obj:myObjectType = new myObjectType();
arr.addItem(obj);
}
function resultHandling(myObject:myObjectType) {
//implementation code to send a request to server side
}
function webServiceResponseHandler(){
//receive the response from server
sendWSRequest();
}
function sendWSRequest(){
if(arr.length > 0)
{
resultHandling(arr.getItemAt(0));
arr.removeItemAt(0);
}
}
Most Flash I/O is asynchronous and there is no way to change that. Consider using inline nested handlers
function checkBoxHandler() {
var obj:myObjectType = new myObjectType();
var resultHandling:Function = function():void {
//you can just reference obj here
var resultHandling2:Function = function():void {
};
};
//implementation code to send a request to server side
}
or use a task scheduler such as the Parsley Command Framework.

How to use Remote Shared Objects

I'm trying to use a remote shared object to send data messages between the client and server using the sharedobject.send method for a chat application.
I am really having difficulty understanding the documentations. The code i attempted to use simply didn't work.
Can someone be kind enough to show me the relevant API calls? And, take a close inspection at my code and tell me were exactly i am going wrong?
Thank You.
Client-Side
import flash.net.NetConnection;
import flash.events.NetStatusEvent;
import flash.events.SyncEvent;
var nc:NetConnection = new NetConnection();
nc.connect("rtmfp://fms/exampledomain");
nc.addEventListener(NetStatusEvent.NET_STATUS, netHandler);
function netHandler(event:NetStatusEvent):void {
if (event.info.code == "NetConnection.Connect.Success") {
trace("Your Connected");
//Creating the sharedobject
var my_so:SharedObject = SharedObject.getRemote("users", nc.uri, false);
my_so.connect(nc);
my_so.addEventListener(SyncEvent.SYNC, syncHandler);
my_so.setProperty("users");
my_so.setDirty("users");
function syncHandler(event:SyncEvent):void {
var changelist:Array = event.changeList;
}
my_so.send function sendMessage(str){
Server-Side
application.onConnect(clientObj)() {
application.acceptConnection(clientObj) {
trace("connected up");
}
var my_so = SharedObject.getRemote("users", false);
my_so.send("Hello user", "Its only a test");
}
After editing your question, it looks like some code is missing in your client-side sample.
From what I see, you don't need the client-side code for your example to work. You can call the send() method directly from the client. But to test whether it works or not, you will need to have two clients connected.
About your NetConnection or your SharedObject, make sure you add your event listeners before you call the connect() method.
If what you want to achieve is simply sharing data between clients, then you don't even need to use the send() method but simply set properties on your shared object. But be aware of the following:
my_so.setProperty("users");
is actually deleting the key users as it is the same as this:
my_so.setProperty("users", null);
Finally, you would need to call setDirty() in very specific cases, but not in this one. Have a look at this answer for a more detailed example on how to share data with SharedObjects