Titanium SDK version: 1.6.1
iPhone SDK version: 4.2
I am developing a Titanium Appcelerator app.
I got a function in a separate file that returns a section for a table view (http://pastie.org/1734554) and on the main file I got a call to this function with a callback. I want to be able to extract the callback data and add it to an array (http://pastie.org/1734548) but I cannot get that data out of the calling function. How is it done?
You’re running into the asynchronous nature of AJAX. Move your alert to within the callback function, and it’ll work as expected:
var rowData = [];
rowData.push("THIS ADDS TO THE ARRAY");
loadPhones(function(data) {
rowData.push(data);
alert(rowData);
});
The reason you have to pass a function to loadPhones is that you don’t want the browser to lock up while you’re retrieving the list of phones. The way you had written it, the anonymous callback function had not been executed by the time you got to the alert statement.
Do whatever you need to do with the retrieved data inside the loadPhones callback.
Related
I have the following code that reads a json file:
Meteor.methods({
getPlaces: function(){
return HTTP.get(Meteor.absoluteUrl("/places.json"), function(e, r) {
console.log(r.data);
return r.data;
});
}
})
The console shows that its retrieving the data just fine.
Here is the part of my helper function for the template I want to display 'Places' in:
testing: function(){
return Meteor.call("getPlaces");
}
& here is my loop in the template where it is supposed to show:
{{#each testing}}
<li>{{testing.name}}</li>
{{/each}}
But it seems like I'm not calling the function right as the loop doesn't show anything. I've tested the loop by giving it random data which it works fine with but whenever I call Meteor.call or even HTTP.get directly on 'testing' it doesn't give me anything.
There are two essential problems here:
You're using HTTP.get asynchronously when you don't need to.
You're trying to use Meteor.call synchronously when you can't as it won't work like that.
Explanation of 1:
In supplying a callback to HTTP.get, you're telling Meteor to allow code execution to continue beyond that line, and pass the result of the call to the callback function. As a result, the actual method function finishes execution and will return undefined (which will be passed back to the calling function as null via EJSON) well before your actual HTTP call has returned. When that happens, the result will be logged, but even though you're returning the results in the callback, the enclosing method function won't care as it will have completed execution long before.
There are several ways to deal with this, the simplest being: don't pass a callback. On the server, you can use HTTP.get synchronously by not passing a callback, in which case code will cease executing until the results come back, and they will actually be returned to the client. Note that you cannot do this if you use HTTP.get on the client. Other ways of dealing with this involve Futures or Promises (better), but are unnecessary here.
Explanation of 2:
This is more complicated to resolve, but is fundamental to Meteor and Javascript. If you're calling an asynchronous function and you want to use the result, you need to supply a callback (or use promises). You can't just expect it to work inline for the same reasons given above. So some changes need to be made:
Don't call a method from within a template helper. You've no idea how often the template helper will run (it depends on all sorts of reactive things), so this is an essentially unbounded amount of traffic on the websocket that you're committing to. Call them when something happens (template is rendered, event handler, a specific piece of data changes (i.e. within an autorun block)), but not in a helper function.
Store the result in a reactive data source, otherwise even if you successfully receive it and put it somewhere, your UI won't update with the results.
So:
Template.yourTemplate.onCreated(function () {
this.places = new ReactiveVar()
Meteor.call("getPlaces", (err, res) => {
// do some error handling here
this.places.set(res)
})
})
{{#each Template.instance.places.get}}
<li>{{name}}</li>
{{/each}}
A few notes:
You need to install the reactive-var package, which inexplicably isn't provided out of the box, for this to work: meteor add reactive-var.
You could return the data via a template helper which uses Template.instance().places.get(), but you can just do it in-line in the template, which seems easier to me.
If the result of the first method call aren't sufficient and you need to update the results, do this in an event handler, or an autorun block as required. If the server needs to be able to push data directly to the client rather than waiting for requests for an update, then methods are the wrong tool - you need to be using Meteor's pub/sub model.
I am dealing with this problem from quite a while, please suggest me a solution:
For android app development, I am using phonegap framework.
I have a async function readFromFile() which reads the json file (which is updated using a user data everytime) stored in SD card, and stores the text result into the global variable, Next I have a function populatePageContents() which reads the global variable, and populates the page's html from the json data.
I want the populatePageContents() function to be called after readFromFile() function has finished reading the file data into global variable.
I tried using someting like this:
<script type="text/javascript">
globalVariable = '';
readFromFile(); // Uses phonegap's file API to read file and puts result into global variable
setTimeout(function() { populatePageContents(JSON.parse(globalVariable)); } , 500);
</script>
The above method works sometimes, but not always. Please suggest some better use of callback functions. Thanks!
Please use callbacks for making it work everytime
U can use something like this:
readFromFile(function(data){
populatePageContents(JSON.parse(data));
});
And your readFromFile function should look like this :
readFromFile(callback){
/***read file phonegap code***//
callback();
}
I'm going round in circles and can't seem to figure out a solution from the resources currently available here on Stack or Google. There's got to be something obvious that I'm missing, perhaps you might be able to help?
Story summary:
A javascript function launches when clicked and creates a new contact in our database.
Additional functions are then called upon successful creation to toggle some settings where necessary, dependant on a few checkboxes.
Calls are currently being made asynchronously, resulting in only the last function call to successfully update the contact.
I can't, for the life of me, get the call to work one after the other instead.
Each call returns a JsonResult upon successful completion, if that helps at all (needed for other areas of the application.
Code currently looks like:
function CreateClicked(){
Contact.Create(**bunch of params**, function(data){
if(data.success) {
togglePrimary(data.newId);
toggleBilling(data.newId);
toggleTechnical(data.newId);
toggleBalance(data.newId);
toggleSecurity(data.newId);
toggleMarketing(data.newId);
Modal.Load(**loads a modal view**);
}
}
}
The toggle functions then look like:
function togglePrimary(id) {
if ($("#contact_admin_primaryrole").prop('checked'))
{Contact.TogglePrimaryRole(id);}
}
Which calls a controller function that looks like this:
public JsonResult TogglePrimaryRole(int contactId){
try{
var c = new Contact(contactId);
c.IsPrimaryContact = !c.IsPrimaryContact;
c.Update(AuthenticatedUser.Username, !c.IsPrimaryContact);
return Json(JSONResponseFactory.SuccessResponse("Contact updated successfully"));
}
catch (Exception ex){
return Json(JSONResponseFactory.ErrorResponse(ex.Message));
}
}
How should I go about setting this up so that each toggle function doesn't start until the previous one has finished and returned a Json response, regardless of success?
Any ideas?
Cheers,
Dez
Using jQuery promises should help:
togglePrimary(data.newId).then(toggleBilling(data.newId)).then(toggleTechnical(data.newId)
etc.
This will run the next function only if the last one was a success. If you want to call the function irrelevent of the outcome then use always() instead of then()
togglePrimary(data.newId).always(toggleBilling(data.newId)).always(toggleTechnical(data.newId)
This will require jquery 1.6 or higher to be referenced. To reference from the CDN add the following
<script src="http://code.jquery.com/jquery-1.9.0.js"></script>
I couldn't seem to get anywhere with promises, or javascript function chaining with callbacks... so I turned the values of each status into an array of strings and parsed it within the controller instead!
Thanks for helping :)
Is there any existing api/code for handling and chaining long-running "async" javascript functions?
First of all I don't think there are any such thing as an asynch function in js right? I guess the only asynch api is the http-request that jQuery uses or am I wrong?
Anyway, when using jQuery to first for instance ping a server, then login, then load a bunch of items etc, it's not very pretty to wrap these functions in each others completed-handler if you know what I mean.
What I have done now is to define a Task-class with some kind of linked-list capabilities, with a task.next-property etc. When I chain these and execute task.run on the first, I have designed it so that each task is run and when its completed-handler is called it runs the task.next task etc.
This works fine but I'm wondering is there is any existing more complete apis for this allready out there I should use?
Maybee with support for cancellation, progress, exception-aggregation etc?
Maybee there are plans for similar async/wait tasks as there are in C# now, but in js?
If you are targetting the browser, you can use setTimeout.. and break up your code into functions, with each iteraction taking one off the stack/queue, and doing that part.. there are also web workers...
If you are doing server-side JS (such as NodeJS), then there are libraries to make async tasks easier to manage. In the browser it takes work... You'll essentially want to create a bundle of tasks, and work them..
function processBundle(tasks, callback) {
var hnd = setTimeout(doWork, 20);
function doWork() {
var item = tasks.shift();
item();
return (
tasks.length
? setTimeout(doWork, 20)
: callback()
);
}
}
//using it...
var items = [];
items.push(simpleFunction1);
...
items.push(simpleFunctionN);
processBundle(items, function(){
alert("done!");
});
The title is the best way I could sum it up. It's a little fuzzy on how to explain this.
Basically, if I include the javascript file holding the JSON function from within an html file, then trigger the JSON function - callback works. However, if I just call the JSON function from within it's own file - callback never fires. Not sure if this is some sort of javascript or web browser security feature. Would be greatful for an explanation.
Here are some examples.
Working version:
json.html: (trimmed down)
<html>
<head><script type="text/javascript" src="json.js"></script></head>
<script>
JSON_Object(json_url, function(json_obj) {
alert(json_obj); // this version works!
});
</script>
<html>
json.js:
function JSON_Object(json_url, callback) {
// This function will return an object to the JSON data.
json_url = json_url + "&callback=jsonCallback";
var json_script = document.createElement("script");
json_script.setAttribute("src", json_url);
json_script.setAttribute("type", "text/javascript");
window.jsonCallback = function(jsonObjReturn) {
// cleanup
document.body.removeChild(json_script);
delete window[callback];
callback(jsonObjReturn); // use our callback function to return the data.
}
document.body.appendChild(json_script);
}
Non-Working version - Another function inside json.js:
JSON_Object(content_page, function(json_obj) {
alert(json_obj); // This version doesn't work. Never called.
});
After a bit of research, I found that I was right. It IS a web browser security feature for extensions.
From the following link:
http://code.google.com/chrome/extensions/content_scripts.html
Quote from the page
However, content scripts have some limitations. They cannot:
Use variables or functions defined by their extension's pages
Use variables or functions defined by web pages or by other content scripts
JSON falls into the above categories because it uses a function for it's callback.