Titanium mvc - call function and wait for result - javascript

I am currently in the process of making my first Titanium iPhone app.
In a model I got:
(function() {
main.model = {};
main.model.getAlbums = function(_args) {
var loader = Titanium.Network.createHTTPClient();
loader.open("GET", "http://someurl.json");
// Runs the function when the data is ready for us to process
loader.onload = function() {
// Evaluate the JSON
var albums = eval('('+this.responseText+')');
//alert(albums.length);
return albums;
};
// Send the HTTP request
loader.send();
};
})();
and I call this function in a view like:
(function() {
main.ui.createAlbumsWindow = function(_args) {
var albumsWindow = Titanium.UI.createWindow({
title:'Albums',
backgroundColor:'#000'
});
var albums = main.model.getAlbums();
alert(albums);
return albumsWindow;
};
})();
however it seems like the call to the model (which fetches some data using HTTP) doesn't wait for a response. In the view when I do the alert it haven't received the data from the model yet. How do I do this in a best-practice way?
Thanks in advance

OK,
Something like this,
function foo(arg1, callback){
arg1 += 10;
....
... Your web service code
....
callback(arg1); // you can have your response instead of arg1
}
you will call this function like this,
foo (arg1, function(returnedParameter){
alert(returnedParameter); // here you will get your response which was returned in above function using this line .... callback(arg1);
});
so here arg1 is parameter (simple parameter like integer, string etc ... ) and second argument is your call back function.
Cheers.

What you need is Synchronous call to web service, so that it will wait till you get the response from the service.
To achieve this in java script you have to pass callback function as parameter and get the return value in callback function instead of returning value by return statement.
Actually coding style you are using is new for me because i am using different coding style.
But the main thing is you have to use call back function to retrieve value instead of return statement. Try this and if you still face the problem than tell me i will try to give an example.

the callback way like zero explained is nicely explained, but you could also try to get it handled with events.
(function() {
main.ui.createAlbumsWindow = function(_args) {
var albumsWindow = Titanium.UI.createWindow({
title:'Albums',
backgroundColor:'#000'
});
var status = new object(), // eventlistener
got_a_valid_result = false;
// catch result
status.addEventListener('gotResult',function(e){
alert(e.result);
got_a_valid_result = true;
});
// catch error
status.addEventListener('error',function(e){
alert("error occured: "+e.errorcode);
git_a_valid_result = true;
});
var albums = main.model.getAlbums(status);
// wait for result
while (!got_a_valid_result){};
return albumsWindow;
};
})();
and your model may something like
main.model.getAlbums = function(status) {
var loader = Titanium.Network.createHTTPClient();
loader.open("GET", "http://someurl.json");
loader.onload = function() {
var albums = eval('('+this.responseText+')');
status.fireEvent('gotResult',{result:albums});
return albums;
};
loader.onerror = function(e){
status.fireEvent('error',{errorcode:"an error occured"});
};
// Send the HTTP request
loader.send();
};

Just as a suggestion, try to use JSON.parse instead of eval as there are risks involved with using eval since it runs all javascript code.

I think that the solution The Zero posted is likely better for memory management, but I'm not totally sure. If you do and eventListener, be aware of the following
(see https://wiki.appcelerator.org/display/guides/Managing+Memory+and+Finding+Leaks)
function doSomething(_event) {
var foo = bar;
}
// adding this event listener causes a memory leak
// as references remain valid as long as the app is running
Ti.App.addEventListener('bad:idea', doSomething);
// you can plug this leak by removing the event listener, for example when the window is closed
thisWindow.addEventListener('close', function() {
// to remove an event listener, you must use the exact same function signature
// as when the listener was added
Ti.App.removeEventListener('bad:idea', doSomething);
});

Related

How to call a inherited function inside a callback function

I have a quick question. I am making video chat with peerjs and I get the error that function is undefined. Here is the code:
Main constructor is Voip which is called in other file like
var voip = new Voip();
This is the function:
function Voip(options) {
var self = this;
options = options ||  {};
var allOptions = _.extend({
secure: true,
debug: 3
}, options);
this.peerjs = new Peer(allOptions);
First problem is here. How could I call otherCall inside callback function with listening to call event Function otherCall is at the bottom. Now it's written with this.otherCall but that doesn't work. I would just like to go to this function whenever I receive call event and answer the call.
this.peerjs.on('call', function(call){
call.answer(window.localStream);
this.otherCall(call);
});
}
And then the Voip is extended with inheriting EventEmitter. Could I get rid of this line completely and still maintain same functionality? I don't use EventEmitter at all but was used in code I helped with.
Voip.prototype = _.extend(EventEmitter.prototype, {
And same here, self.otherCall doesn't work. What is the solution?
callOther: function(receiverId) {
var self = this;
var call = self.peerjs.call(receiverId, window.localStream);
self.otherCall(call);
},
otherCall: function(call) {
if (window.existingCall) {
window.existingCall.close();
}
call.on('stream', function(stream){
$('iframe').putthecodein....(stream)
});
window.existingCall = call;
}
});
Hope I've been clear in my question. If I summarize I want to call function otherCall once when listening to call event and second time inside callOther function. And for inheriting EventEmitter I wonder if I can modify code in such way that I don't need that line and everything still works.
Use like this. It might work.
var self = this;
self.peerjs.on('call', function(call){
call.answer(window.localStream);
self.otherCall.call(self, call);
});

How am I getting the scope wrong? Unable to access this function from jQuery ".on"

Morning all,
I'm using the following code to somewhat imitate setInterval with AJAX:
// Poll for ALERTs
(function pollForAlerts() {
var params = { "send": 1, "poll": 1 };
// Set up the correct patch for sending AJAX data
ALERTS = {};
ALERTS.Auth = { site: data_site, uuid: data_uuid };
ALERTS.API = function(app,data) {
var url = "//myurl.com/alerts/"+ app +"/?";
var data = $.extend({}, ALERTS.Auth, data);
return url + jQuery.param(data || "") + '&timestamp='+$.now();
}
// Run the AJAX request
$.getJSON( ALERTS.API( 'touchscreen', params ) , function (response) {
if( typeof response === "object" ) {
for( var i = 0; i < response.length; i++ )
renderAlert(response[i]);
} else { setTimeout( pollForAlerts, 3000 ); }
});
}());
The function runs repeatedly until it finds a response.
I'd like to then set a jQuery ".on" to restart this loop if a certain element is clicked on:
// Respond to ALERT
$('#alerts').on('click', 'td.initials span', function(event) {
$(this).closest('tr').removeClass("active abs cri");
pollForAlerts();
});
However, when I do that, I get the following error in Firebug:
ReferenceError: pollForAlerts is not defined
http://myurl.com/alerts/static/js/touchscreen.js
Line 14
I can't work out why pollForAlerts() can't be accessed. Is it because of the self-executing function, or is it just because it's being used within jQuery's on function?
I'm not JavaScript expert, especially when it comes to self-executing functions and closures, so please be gentle with me!
Duncan
You wrote self invoking function, These functions are executing only once in lifetime. If you want to call a function multiple times then you can write it as normal function.

JavaScript Too much recursion error with facebook JavaScript SDK

i am sending FB.login request to facebook. but FB is not defined while javascript SDK is still loading core javascript resources.
so, i put a check to get FB variable
function check_FB_variable(){
if(typeof FB=='undefined'){
check_FB_variable();
}else{}
}
check_FB_variable();
But this approach gives me Too much recursion error.
so , i put this code as
function check_FB_variable(){
if(typeof FB=='undefined'){
setTimeout(check_FB_variable,600);
}else{}
}
check_FB_variable();
but in this approach the before timeout function make a call function moves down and gives error
FB.login not defined.
please, help.
I've used something similar to check if JQMobi exists, I don't know exactly why but I think the exception is thrown because you call the pointer to the function every time.
You should try checking in an interval like this (Untested):
var facebookChecker = window.setInterval(fbCheck, 200);
var fbCheck = function () {
if (typeof FB != 'undefined' && facebookChecker != null) {
window.clearInterval(facebookChecker);
facebookChecker = null;
// Whatever you want to do if facebook is loaded
// Example: InitFBLogin();
}
}
Or you could use a while statement (the one I used):
/*
* This JQ Fix tries to attach Jquery to a variable to ensure it exists.
* - Marvin Brouwer.
*/
var FixJQ = function () {
var JQFIX = null;
while (!JQFIX || JQFIX == null) {
try {
JQFIX = jQuery;
} catch (nothing) { jQuery = $; };
};
JQFIX = null;
return true;
};
if (FixJQ()) {
FixJQ = null;
};
The beauty of the last one is that you can put you next step below this, because it will wait until the while loop has finished.
I honestly do not know which one is better/faster but I’m sure the bottom one will work.

Variable scope in Javascript Object

I'm discovering the concept of "objects" in JavaScript. I'm making an RSS Parser, and I have an error (commented).
function MyParser (feed_url) { // Construct
"use strict";
this.feedUrl = feed_url;
this.pubArray = [];
if (typeof (this.init_ok) == 'undefined') {
MyParser.prototype.parse = function () {
"use strict";
var thisObj = this;
$.get(this.feedUrl, function (data, textStatus, jqXHR) {
if (textStatus == 'success') {
var xml = jqXHR.responseXML,
//lastBuildDate = new Date($(xml).find('lastBuildDate').text());
items = $(xml).find('item');
items.each(function () {
var pubSingle = thisObj.makeObj($(this).find('pubDate').text(),
$(this).find('link').text(),
$(this).find('title').text(),
$(this).find('description').text(),
$(this).find('encoded').text(),
$(this).find('commentRss').text(),
$(this).find('comments').last().text());
thisObj.pubArray.push(pubSingle);
});
console.log(thisObj.pubArray); // OK
}
}, 'xml');
console.log(this.pubArray); // Empty
return (this.pubArray);
};
MyParser.prototype.makeObj = function (pubDate, pubLink, pubTitle, pubDesc, pubContent, pubComCount, pubComLink) {
"use strict";
var pubSingle = {};
pubSingle.pubDate = new Date(pubDate);
pubSingle.pubLink = pubLink;
pubSingle.pubTitle = pubTitle;
pubSingle.pubDesc = pubDesc;
pubSingle.pubContent = pubContent;
pubSingle.pubComCount = pubComCount;
pubSingle.pubComLink = pubComLink;
return (pubSingle);
};
}
this.init_ok = true;
}
If you look at the console.log(), you'll see that the line // OK is outputting my array correctly.
But later, when returning from $.get, my array is empty.
Does anybody have an idea why, and how to correct that please?
This is not a problem with variable-scope. The problem here is that you're working with asynchronous flow and you're not thinking correctly the flow.
Let me explain:
When you do your .get, you fire a parallel asynchronous process that will request information from the browser, but your main program's flow keeps going, so when you get to your "return" statement, your array has not been filled yet with the response from your get method.
You should use your array from inside the get callback and not outside of it, since you can't guarantee that the array will have the information you need.
Does it make any sense?
Let me know!
Further explanation
According to your comments, you're still doing something like this:
var results = MyParser(feed_url);
//code that uses results.pubArray
And you cannot do that. Even though you're setting your "pubArray" inside your .get callback, you're trying to use pubArray right after you called MyParser and that's before the .get callback is called.
What you have to do, is call your next step on your program's logic from within the .get callback... that's the only way you can be sure that the pubArray is filled with proper data.
I hope that makes it clearer.
This is because your line
console.log(this.pubArray); // Empty
is being called directly after you issue your Ajax request; it hasn't had time to fetch the data yet. The line
console.log(thisObj.pubArray); // OK
is being called inside the Ajax callback, by which time the data has been fetched.
Thank you all, and particulary #Deleteman .
Here is what I did:
$.get(this.feedUrl, 'xml').success(function () {
thisObj.handleAjax(arguments[0], arguments[1], arguments[2]);
$(document).trigger('MyParserDone');
}).error(function () {
$(document).trigger('MyParserFailed');
});
Then, when i enter "HandleAjax", i'm back in my object context, so "this" refers to my object and the right properties. The only "problem" is that I have to set a listener (MyParserDone) to make sure the parsing is finished.

Open social viewer state (isOwner)

We are creating a gadget for the opensocial API 0.7.
In some functions we have to decide, if the viewer is the owner.
We couldn't use the usual function for this purpose:
return gadgets.util.getUrlParameters().viewer == gadgets.util.getUrlParameters().owner;
so we had to create a workaround and get the information via a DataRequest.
The DataRequest calls a callback function and has no useable return value.
We tried a quick hack by using global variables to set the corresponding value.
The issue at this point is, that the function does not 'wait' for the callback-function to be finished. We know this is no good code/style at all, but we tried to force a timeout for debug reasons.
Handling all the code within the callback-function (as suggested in the examples of the opensocial docs) is not possible.
We are looking for something like a real 'sleep()' in JavaScript to wait for the callback-function to complete or another alternative to get the owner information about the viewer.
globalWorkaroundIsOwner = false;
function show_teaser(){
if (current_user_is_owner()){
// ...
}
// ...
}
function current_user_is_owner() {
var req = opensocial.newDataRequest();
req.add(req.newFetchPersonRequest(opensocial.DataRequest.PersonId.VIEWER), 'viewer');
// This will set the the correct value
req.send( user_is_owner_workaround );
// This is an attempt to delay the return of the value.
// An alert() at this point delays the return as wanted.
window.setTimeout("empty()", 2000);
// This return seems to be called too early (the variable is false)
return globalWorkaroundIsOwner;
}
function user_is_owner_workaround(dataResponse) {
var viewer = dataResponse.get('viewer').getData();
globalWorkaroundIsOwner = viewer.isOwner();
// value is correct at this point
}
Can you use an additional flag in order to indicate whether the remote query has already returned the required value?
var globalWorkaroundIsOwner = false;
var workaroundStarted = false, workAroundComplete = false;
var checker;
function show_teaser(){
if (!workaroundStarted) {
workaroundStarted = true;
current_user_is_owner();
}
if (workaroundComplete) {
if (globalWorkaroundIsOwner){
// ...
}
// ...
if (checker) {
clearInterval(checker);
}
}
}
function current_user_is_owner() {
var req = opensocial.newDataRequest();
req.add(req.newFetchPersonRequest(opensocial.DataRequest.PersonId.VIEWER), 'viewer');
checker = setInterval("show_teaser()", 1000);
// This will set the the correct value
req.send( user_is_owner_workaround );
}
function user_is_owner_workaround(dataResponse) {
var viewer = dataResponse.get('viewer').getData();
globalWorkaroundIsOwner = viewer.isOwner();
workAroundComplete = true;
// value is correct at this point
}

Categories

Resources