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

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.

Related

Postman - Populating objects and array from secondary requests in Tests

I am trying to understand how pm.sendRequest works.
I have a query and some Tests code that loops through a response and makes a second request using pm.sendRequest with variables from the initial response.
This works, but within the pm.sendRequest I have a loop that creates an array that I need to push into a global array (defined before pm.sendRequest is called).
The problem is the pushing doesn’t work. I end up with an empty array.
So my question is: are variables that are supposed to be global (to the code, not Postman global variables) not available inside pm.sendRequest?
Code sample:
let myArray = [];
pm.sendRequest( url, function (err, response){
let foo = [];
//a for loop here that push populates foo[] just fine
for {
foo.push(name);
} //end loop
console.info(foo); //all good
myArray.push(foo);
console.info(myArray) //all good
});
console.info(myArray); //empty
foo array is properly populated at the end, but my Array is empty.
Any ideas?
Thanks.
After much searching on this, I found that the pm.sendRequest method is an asynchronous request: https://learning.postman.com/docs/writing-scripts/script-references/postman-sandbox-api-reference/#sending-requests-from-scripts:~:text=You%20can%20use%20the%20pm.sendRequest%20method%20to%20send%20a%20request%20asynchronously%20from%20a%20Pre%2Drequest%20or%20Test%20script.
That means it's not a blocking request and it runs in the background, independently of the code that follows it.
As such, the last console.log. output does not wait for the pm.sendRequest function to complete and populate the array.
Some workarounds for this are possible, using global variables and promises:
https://community.postman.com/t/can-i-use-pm-sendrequest-to-send-requests-synchronously/225/14
https://james.jacobson.app/javascript-promises-and-postmans-sendrequest/
https://stackoverflow.com/a/53934401/10792097
However, overall, it looks like this is not the way to go about it in the Postman universe.
I think we're supposed to split out each request by itself and use request workflows: https://learning.postman.com/docs/running-collections/building-workflows/
This is what I'll be trying to do next.

Array contents don't change when used in a function in AJAX success

I'm using AJAX to parse json, everything works fine although when I try to call a function where I pass on the index value of the loop and then the funcion pushes this value into the Global array, It seems like it's not pushing these values even though console.log() prints out everything as it should on each step yet when I check the array length it's always at 0.
//This bit of code is actually inside a function and another ajax success
$.each(updatedData, function(index, element) {
$.ajax({
type: 'GET',
url: 'https://en.wikipedia.org/w/api.php?action=query&prop=extracts&exintro&explaintext&format=json&redirects&callback=?&titles='+updatedData[index],
data: { get_param: 'value' },
dataType: 'json',
success: function (data) {
console.log('success wiki');
//getting the pagenumber key value to use the extract value from it
var pagenum = Object.keys(data.query.pages);
if(data.query.pages[pagenum[0]].extract == null)
{
recycleData(index);
}
}
});
});
//this is the function which should push the trash value to the trashData
//the console.log actually logs all the correct values yet still doesn't push
function recycleData(trash){
console.log("sliced - "+trash);
trashData.push(trash);
}
//that's how the array is defined, it's obviously defined before all the functions just in case someone would ask
var trashData = [];
UPDATE: I have tested the array length after a delay, it is being populated after all the ajax requests have been completed yet I need other requests to wait for this request to finish so that the other requests will use updated data. This request is in another ajax success so keep that in mind.
If you want do other ajax on completion you can call your next ajax call on done function of previous ajax.
$.ajax({ ... }).done(second_ajax_fn);
Also you can set async:false but it is not recommended
The issue is likely that you're checking the value of trashData before the actual async requests are complete. If you want to ensure that all the requests are complete and then check it (or make more requests), you need something like jQuery Deferred - waiting for multiple AJAX requests to finish.

How to understand asynchronous Meteor.call on the client side

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

Calling a Oracle Service through JSON inside another JSON service call

I am using JSON to call a oracle service which fetches data (a list) from Server side. Inside the JSON call, I iterate through the list in a FOR loop and I need to call another web service to fetch some data related to each row in the list. I have written that logic in a JavaScript function, and I am calling that function inside the FOR loop.
The issue I am facing is, for example, if original list consists of a 2 items, then the FOR loop is executing 2 times first, and then the function is called 2 times together.
What I want is that every time the loop is iterated, the java script method should be called in the same iteration, fetch the values, I should be able to perform actions based on the results fetched, and it should be repeated for next iterations.
The javascript code is given below. Sequence of alert I am want is: 1,3,2,4,1,3,2,4,5.
Sequence of alert which I am getting is: 1,4,1,4,3,2,3,2,5
var url = "idcplg?IdcService=AJAX_SERVICE&Id=" + Id;
$.getJSON(escapeCachingForUrl(url), function(data){
for(var i=0;i<data.length;i++){
alert("1");
//some code written
myMethod(data,function(result){
alert("2");
// Need to operate on result
});
alert("4");
}
alert("5");
});
function myMethod(data,callback){
var url = "idcplg?IdcService=AJAX_ANOTHER_SERVICE";
$.getJSON(escapeCachingForUrl(url), {
data : data
}, function(data){
alert("3");
callback(data);
});
}

javascript Scope issue in Ext.Ajax.request

I have this class which automatically generates JSON stores from server. Stores are created on the fly and are viewable in Firebug's DOM.
I think i have some scope issues.When i want to console.log(Ext.getStore('AdminSettings')) (which AdminSettings is one of the created stores) inside Ext.Ajax.request`s callback function, 'AdminSettings' store is returned but if i put console.log(Ext.getStore('AdminSettings')) everywhere outside callback function,i get undefined message in firebug and my store is not instantiated.
See code comments to see it in action.
Ext.Ajax.request({
url : './account/getadminstores',
callback : function(options, success, response) {
var json = Ext.decode(response.responseText);
var adminStores = new Array();
// setup and intitialize on the fly stores
for ( var key1 in json) {
var storeFields = new Array();
for ( var key2 in json[key1]) {// if (i==1){break;}
for ( var key3 in json[key1][key2]) {
storeFields.push(key3);}
break;};
Ext.define('MA.store.' + key1, {
extend : 'Ext.data.Store',
fields : storeFields,
storeId : key1,
data : json[key1]
});
Ext.create('MA.store.'+key1);};
console.log(Ext.getStore('AdminSettings'));
//returns MA.store.AdminSettings in firebug and everything is fine
}
});//eof Ext.Ajax.request
console.log(Ext.getStore('AdminSettings'));
//returns undefined which is strange
Ext.Ajax.request is asynchronous. Your final console.log call returns nothing because at the time it is executed the request has not yet completed. Any code depending on the results of the request's callback will need to be executed by the callback as well.
This is due to the asynchronous nature of javascript, callback doesn't get called until after the ajax request finishes, so the Ext data store doesn't get defined either.
The order of execution of your script is:
Ajax request is initialized
console.log is executed (undefined since the datastore hasn't been defined yet)
javascript is waiting for an ajax response from ./account/getadminstores
response comes in, callback function is called (which defines the data store)

Categories