How to understand asynchronous Meteor.call on the client side - javascript

I'm working on a Meteor project and want to get the return value of Meteor.call in template helpers on client side. At very first, I just set a variable in the call back function and get the variable's value outside the Meteor.call. I found out the code after Meteor.call doesn't execute at all. Then I searched a bit and use Session, it works. But I don't really understand the reason. Here's my original code and modified code. Can anyone explain a bit for me? Thanks!!
Original wrong code: html
<div id="text-result-main">
<h2>{{title}}</h2>
</div>
js
Template.texts.helpers({
title: function(){
var index = Router.current().params.index;
Meteor.call('getTitle', index,function(error, result){
titles = result;
});
console.log(titles);
return titles;
}});
Collection text.js
Text = new Mongo.Collection("text");
Meteor.methods({
'getTitle': function(myindex){
return Text.findOne({index: myindex}).title;
}});
The working code: js
Template.texts.helpers({
title: function(){
var index = Router.current().params.index;
Meteor.call('getTitle', index,function(error, result){
Session.set("titles",result);
});
console.log(Session.get("titles"));
return Session.get("titles");
}});
Notice that I didn't publish Collection Text to the client at all because it's just so huge. Every time when I refresh the page when running the wrong code, I can't see the content of "title" or see it on the console. But when I set the session, it works. I don't really understand how it works here. Thanks

There is two issues Asynchronicity and Reactivity
This affectation
Meteor.call('getTitle', index,function(error, result){
titles = result;
});
inside the meteor call is executed but in a asynch way. So the return of your helper is immediately called, and return a empty value.
Try it out in the console of your browser.
But then, why your template render correctly with {{title}} when you use a Session Variable ?
It's because the Session is a reactive data source, witch means that every change to it trigger a re-computation of all templates involving this piece of data.
Here is a timeline:
Methods is called
Return empty value
Method is executed, setting variable value
If the Variable is a reactive data source, template is re-computed. ( in your case, the session is a reactive data source. )
To go further
I would use a reactive var in that case, it's very close from a session variable, but the scope is limited to a template.
A good read on Reactive data source: http://richsilv.github.io/meteor/meteor-reactive-data-types/

The problem is the fact that Meteor.call() is asynchronous when paired with a callback.
So when title() starts executing, it does not wait for your Meteor.call() invocation to return a result (or possibly an error). It continues execution. This is called asynchronous execution.
In short, you are trying to log the value for the key titles which doesn't exist in Session (since the state of your asynchronous Meteor call is unknown, at this point of time).
Try moving the console log statement into the callback paired with your Meteor.call() and you can see the result once it has successfully been set in Session.
A workaround to your problem is to make your Meteor.call() synchronous like this:
Template.texts.helpers({
title: function(){
var index = Router.current().params.index;
var result = Meteor.call('getTitle', index); // <--- this is synchronous code now
Session.set("titles",result);
console.log(Session.get("titles"));
return Session.get("titles");
}});
Removing the callback makes Meteor.call() behave synchronously.
If you do not pass a callback on the server, the method invocation
will block until the method is complete. It will eventually return the
return value of the method, or it will throw an exception if the
method threw an exception.
(from http://docs.meteor.com/api/methods.html#Meteor-call)

Why not use something like this:
title: function(){
var index = Router.current().params.index;
var a = Text.findOne({index: myindex}).title;
console.log(a);
return a;
without methods

Related

Changing the value of a property of an object inside a function in java script doesn't work as it should

I have two modules and in the first one I declare an object because I know that primitives are passed by value in java script and objects by reference.I want to get the response status from a request and I am passing the object as a reference so I will be able to modify its property.The problem is that it doesn't do anything.In the end the value would be the same.
//this code is in a different module from the other one
var variableToBeChanged = { something : "Initial value" };
anotherModule.changeValue(variableToBeChanged);
alert(variableToBeChanged.something);
//and in the other module I have a $.ajax and I want to get the response status(ex. 200)
//the code is sth like this:
function AnotherModule(ajax){
function changeValue(variableToBeChanged){
...
...
...
$.ajax({
...
...
...
success: function(data,xhr){
variableTobechanged.something = xhr.status;
}
});
}
}
In the end it will display: "Initial value" instead of 200 or anything else.
What am I doing wrong here?
The ajax call is asynchronous and therefore the alert gets called before the variable is modified. You can use promise in ES6 like this to make sure it is executed after ajax call completes.
new Promise((resolve) =>{
anotherModule.changeValue(variableToBeChanged);
resolve();
}).then((res) =>{
alert(variableToBeChanged.something);
}).catch((error) =>{
alert(error);
});
In javascript copy of reference to object is passed.
This means that any changes made to the object will be visible to you after the function is done executing.
Since javascript is asynchronous , alert(variableToBeChanged.something) this line gets executed before your function returns . Therefore you see old value . You have to use callbacks or promise to work synchronously.
Please refer to this question javascript pass object as reference .It explains this concept beautifully.

JSONP callback out of order

I apologise if this is a repeated question. I have done my research but I am still unable to come up with an answer:
I am writing a small JSONP API. The desired outcome:
The programmer creates an instance of the JSON class.
var json = new jsonpRequest (
"http://url.to.web_service",
searchParam //passed in as an associative array
);
The programmer then declares var myJSON = jsonpRequest.getJsonP()
This will return the JSONP into the myJSON variable to be processed.
The API successfully takes in the parameters when the instance of the class is constructed, generates a link to the services w/ the desired parameters and injects it into the DOM via script tags using this function:
jsonpRequest.prototype.fetchJsonp = function(){
var request = that.getRequest();
//creates a script element
var jsonpCall = document.createElement('script');
//gets jsonp request
jsonpCall.src = request;
//adds script to DOM, which goes on to request the JSONP from the service.
document.head.appendChild(jsonpCall);
};
EDIT: For the sake of clarity: this definitely does return the data set that is requested.
The function I am using as my callback is:
jsonpRequest.prototype.processCallback = function(data){
alert(data);
that.setListOfResults(data);
};
It is called in the link as: &callback=jsonpRequest.prototype.processCallback
The processCallback function takes in the data from the request and stores it in a variable within the class using the setListOfResults function.
After the data has been stored I would like to return the data from the request to the myJSON variable. This is the getJsonp function that the programmer invokes in step 2 of the process:
jsonpRequest.prototype.getJsonp = function(){
that.buildRequest();
that.fetchJsonp();
return that.listOfResults;
};
The issue:
Here is what I am calling after I define my JSON API class:
var myJSON = json.getJsonp();
console.log(myJSON);
Because it is asynchronous, it is moving on to the console log before the callback function is invoked, resulting in MYJSON to be undefined. I am not sure how I can daisy chain my code together in such a way where it runs the callback before moving on.
I tried to emulate the solution from How can I get this JSONP call to return a value?. I may be missing a step or I am perhaps ignorant of something! Any help to clarify this would be greatly appreciated. Thank you in advance :).
P.S. I would like to avoid using JQuery etc... It would be beneficial to keep this strictly as raw JavaScript.
I am not sure how I can daisy chain my code together in such a way where it runs the callback before moving on.
You can’t do that. You must rely on callbacks. Add a callback parameter to your jsonpRequest constructor, and invoke that callback after you do setListOfResults(data). You may also wish to allow for setting the this context in which the callback will be executed.
jsonpRequest.prototype.processCallback = function(data){
alert(data);
that.setListOfResults(data);
that.callback.apply(that.context, [data]);
};

javascript can't assign variable values due to asynchronous function.

In my JavaScript code below, I cannot get user_likes to take the value of response. I can Console output "response", and it has the values I need. However, when I try to Console output user_likes, I get undefined. What gives?
function fqlUserLikes(user_id) {
var user_likes;
FB.api(
{
method: 'fql.query',
query: 'SELECT page_id,name,type FROM page WHERE page_id IN (SELECT page_id FROM page_fan WHERE uid=' + user_id + ')'
},
function(response) {
user_likes=response;
}
);
Console.log(user_likes);
return user_likes;
}
Thanks for any help,
I.N.
Your method is asynchronous, so when you try to log and return the variable, it hasn't actually been assigned yet. Take the below snippet:
//Do what you need to do in here!
function(response) {
user_likes=response;
});
//The below code is executed before the callback above.
//Also, it should be console, and not Console. JS is case sensi.
Console.log(user_likes);
return user_likes;
Your function that changes the value of user_likes is not called immediately. FB.api() starts the process of calling the API, and then returns. Your console.log() call happens at this point, and then your function returns the user_likes value to its caller. Then, at some later point, the api call completes, your callback function is called, and it sets user_likes to the appropriate value. Unfortunately, there's nothing left to read that value.
You need to completely restructure your code to make it work. Javascript doesn't really support waiting for things to happen in the middle of code; it's a very asynchronous language. The best way is if you pass a callback function that actually uses the new value, rather than trying to store it in a variable for something else to read.

Initializing a variable declared outside of jQuery $.getJSON callback function

My script makes an Ajax request through jQuery. The data received is json_encoded via a server side script (php). This is further, stringified and then parseJSON'ed using jQuery to yield a recordCollection. I loop through recordCollection and push it onto records array, the variable that was declared outside the scope of the callback function.
The function looks like this:
var records = [] ;
$.getJSON(url, {id : recordid}, function(data) {
var recordCollection = jQuery.parseJSON(JSON.stringify(data));
$.each(recordCollection, function(){
records.push(this);
});
console.log(records) // displays all the objects, thus, the above code is right
});
console.log(records); // displays []
As you can notice above, the second time I logged records onto the console, it yielded an empty array, which is leading me to two conclusions:
Javascript arrays are passed by value, hence records outside the scope of the callback function does not retain it's value.
Ajax is asynchronous, hence records is being logged before the ajax call was complete and hence it still retains the value of the empty uninitialized records array and is logging that onto the console.
Now if 1 is true, how can I initialize the records array ?
And if 2 is true, should I make this Ajax call synchronous? This will make javascript block until the value is returned and thus the second time records array is logged onto the console, it will display the updated value of the array ?
The third is that I am completely missing a trick and doing something really dumb here.
I do need the records array to be set with the data returned from the ajax call as it needs to be passed around to different javascript functions in the script and the DOM basically waits for this result to load the data.
Thanks guys!
You are correct in that, ajax calls are async, hence Asynchronous Javascript and XML. You can make it sync, but you shouldn't because your array will then be available for anyone to play with, which can cause some big headaches.
Instead, have an init call that is run once your ajax call is completed.
function callback(records) {
// do stuff with records.
}
$.getJSON(url, {id : recordid}, function(data) {
var recordCollection = jQuery.parseJSON(JSON.stringify(data));
callback(recordCollection);
});
Ajax stands for Asynchronous JavaScript and XML (forget the XML part) so i think you can guess what is right ;-)
I added the execution order with comments:
var records = [] ;
// 1. send request and return immediately
$.getJSON(url, {id : recordid}, function(data) {
// 3. response from server is received
var recordCollection = jQuery.parseJSON(JSON.stringify(data));
$.each(recordCollection, function(){
records.push(this);
});
console.log(records)
});
// 2. print records
console.log(records);
You should not try to make the request synchronous in any way. If you need to do something with records after the data is available you should create a new function and call it after you pushed the values into records.

jQuery function execution order

I am having a problem, or perhaps a lack of understanding, with the jQuery execution order of $.get() function. I want to retrieve some information from a database server to use in the $.ready() function. As you all know, when the get returns, it passes the data to a return handler that does something with the data. In my case I want to assign some values to variables declared inside the ready handler function. But the problem is, the return handler of $.get() does not execute until after ready has exited. I was wondering if (a) am I doing this right/is there a better way or if (b) there was a way around this (that is, force the get return handler to execute immediately or some other fix I'm not aware of). I have a feeling this is some closure thing that I'm not getting about JavaScript.
As per request, I'll post an example of what I mean:
$(function() {
var userID;
$.get(uri, function(returnData) {
var parsedData = JSON.parse(returnData);
userID = parsedData.userID;
});
});
So as you can see, I'm declaring a variable in ready. Then using a get call to the database to retrieve the data needed. Then I parse the JSON that is returned and assign the userID to the variable declared before. I've tested it with a couple alerts. An alert after the get shows userID as undefined but then an alert in get's return handler shows it to be assigned.
$.get() is asynchronous. You have to use a callback to fill your variable and do the computation after the request is complete. Something like:
$(document).ready(function(){
$.get( "yourUrl", function( data, textStatus, jqXHR ) {
var myData = data; // data contains the response content
// perform your processing here...
registerHandlers( myData ); // you can only pass "data" off course...
});
});
// your function to register the handlers as you said you need to.
function registerHandlers( data ) {
// registering handlers...
}
$.get is an ajax request. A in AJAX stand for asynchronous, so script won't wait for this request to finish, but instead will proceed further with your code.
You can either use complete callback or you can use $.ajax and set async to false to perform synchronous request.
The $.get() function executes an async httprequest, so the callback function will be executed whenever this request returns something. You should handle this callback outside of $.ready()
Maybe if you explain exactly what do you want to do, it would be easier to help!
Are you looking for something like:
$(document).ready(function(){
var variable1, variable 2;
$.get('mydata.url', function(data){
variable1 = data.mydata1;
variable2 = data.mydata2;
});
});
If you declare the variables first, then you can set their values within the get call. You can add a function call at the end of the get handler to call a separate function using these values? Without some kind of example, its hard to go into any more detail.
Without seeing the full code, my guess is that you should declare your variable outside $.ready; initialize it in ready for the initial page load; then update it from the get callback handler.
for example
var x = ""; // declaration
$(document).ready(function() { x = "initial value"; });
$.get(...).success(function() { x = "updated from ajax"; });

Categories