global variables

Dec 21, 2012 at 4:22 PM

To address a particular situation occurring in the asynchronous processing of a current project, I would like to have a set of values that I build dynamically throughout the routine where these values are available at the end of the application. I set two global variables, then play with them in one routine. Within that routine (doStuff), the values are available as expected. 

But I want that global variable available when the whole process ends. No matter how wonderfully the values appear during doStuff, by the time the process has finished, there's nothing available.

I've worked out all the situations regarding asynchronous processing to the extent I understand them and am having no luck. Any ideas?

<script language="javascript" type="text/javascript"> 

	var devGFSSEForms = function() {
		var arHoldAll = [];
		var arCurrentDupes = [];
...
		//as it stands, this first routine runs whenever the page is accessed
		$(document).ready(function() {  
...
			$().SPServices({
				operation:  "GetListItems",  
				listName: "{D3795486-9926-424E-8F14-59BE5DB65BA8}",	//dev GFSSEForm
				CAMLViewFields: "<ViewFields><FieldRef Name='LinkFilename'/><FieldRef Name='Exported' /></ViewFields>", 
				completefunc: function (xData, Status) { 
...
				}  //completefunc
			});  //SPServices
		});  //document.ready

	return {

		processRecord: function () {
			var myQueryOptions = "<QueryOptions />";
			var myQuery = "<Query><OrderBy><FieldRef Name='Created_x0020_Date' /></OrderBy><Where><Eq><FieldRef Name='Exported' /><Value Type='Integer'>0</Value></Eq></Where></Query>";	//user, action but no attention to Orphan status
			$().SPServices({
				operation:  "GetListItems",
				listName: "{D3795486-9926-424E-8F14-59BE5DB65BA8}",	//development
				CAMLViewFields: "<ViewFields><FieldRef Name='LinkFilename'/><FieldRef Name='Exported' /></ViewFields>", 
				CAMLQuery: myQuery,
				CAMLQueryOptions: myQueryOptions,
				completefunc: function (xData, Status) {
					$(xData.responseXML).SPFilterNode('z:row').each(function() {
...
					});  //each()
					$.each(arIDs, function(index, value) {
						var promise = $.ajax({
							type:"GET",
							url:"GFSSEForm/" + arIDs[index][2],
							dataType:"text"
						});
						promise.done(doStuff);	//magically passes the data along, too
						promise.fail(function() {alert("failed to open this eform: " + arIDs[index][2]);});
					});  //each
				}  //completefunc
			});  //SPServices
			// would like arCurrentDupes to be available here
		}  // processRecord()
	};	// public method processRecord() [return]
			
		function doStuff(data) {
...
				var useThis = arValues;
				if(arValues[0] != "") {
					var strName = arValues[0];
					var strAction = arValues[5].substring(0,1);
					var strCombo = strName+":"+strAction;
					if($.inArray(strCombo, arHoldAll) > -1) arCurrentDupes.push(strCombo);
					else arHoldAll.push(strCombo);	//this works
					findExisting(strName, strAction);	// calls SPServices
				}	// if username wasn't empty
				arValues = useThis;	//this works
...
					writeNew();
...
		}  // doStuff()

		function writeNew() {
...
			arValues = strUseThis.split(",");
			for(i=0; i<iFields; i++) {
				strTest = arValues[i];
				if(strTest.length > 255) strTest = strTest.substring(0,255);
				strField = arFields[i]; 
				vpairs.push([strField,strTest]);
			}
			$().SPServices({
				operation: "UpdateListItems",
				batchCmd: "New",
				listName: "{37229D49-3F6D-4812-A2F0-A0DC55B89DD1}",  //development list
				valuepairs: vpairs,
				completefunc: function(xData, Status) {
...
				}	//completefunc
			}); //SPServices

		}  // writeNew()

...
		function findExisting(user, action) {
			var queryOptions = "<QueryOptions />";
			var query = "<Query><Where><And><And><Eq><FieldRef Name='Title' /><Value Type='Text'>" + user + "</Value></Eq><BeginsWith><FieldRef Name='col05x' /><Value Type='Text'>" + action + "</Value></BeginsWith></And><Neq><FieldRef Name='Orphan' /><Value Type='Integer'>1</Value></Neq></And></Where></Query>";  
			//CAML looks for existing items having same name and action, ignoring any that have already been marked as orphans
			$().SPServices({
				operation:  "GetListItems",
	      async: false,		//required!!!!! ; not used: no stack overflow, runs Orphan; use: stack overflow, no Orphan
				listName: "{37229D49-3F6D-4812-A2F0-A0DC55B89DD1}",	//development GFSSVerified list
				CAMLViewFields: "<ViewFields><FieldRef Name='Title'/><FieldRef Name='col05x' /></ViewFields>", 
				CAMLQuery: query,
				CAMLQueryOptions: queryOptions,
				completefunc: function (xData, Status) {
					var iCount = parseInt($(xData.responseXML).SPFilterNode("rs:data").attr("ItemCount"));
					if(iCount > 0) {...}
...  //in the current data set I'm using, there is never a reason to come here
				}  //completefunc
			});  //SPServices
		}

...
  }(); //devGFSSEForms

</script>  

"divId">

<button id="btnInitiate" onclick="devGFSSEForms.processRecord()">Click, to export to SharePoint</button>

"showErrors">
"results">


Dec 21, 2012 at 6:50 PM
Edited Dec 21, 2012 at 6:51 PM

I'm assuming your two "global" variables are (Note, they way this code is written, these variables are not global): 

var arHoldAll = [];
var arCurrentDupes = [];

If you want these to be available after your routine is finished, you should add them to your `return` statement as a property.  They are currently private since they are contained within a function and are created using the `var` statement.

return {
    processRecord: function() { 
        // do something awesome
    },
    arHoldAll : arHoldAll,
    arCurrentDupes : arCurrentDupes
};

 

Cheers,
Matthew 

Dec 26, 2012 at 7:34 PM

Thank you for that, Matt. Unfortunately, I had no more luck with that approach than what I had been using, but I certainly had not been aware of the possibility of making those variables into properties.

The use (and desire to expose) the global variables were one approach that has now been superseded. By tracking what's been written (by populating an array) and checking that array (rather than the SharePoint list), I'm able to achieve my goal. For whatever reason, it appears that calling findExisting for each email message is "too much," and by saving the call for only when the array shows an existing item, the system appears to be able to keep up.

It's quite possible, too, that our environment (of IE7 and Windows XP) is contributing to odd behavior. I had earlier written of a phenomenon (stack overflow) that happens only within that environment and not, say, using IE8 with MOSS.

Coordinator
Dec 27, 2012 at 10:41 PM

I'm only skimming the thread, but if you define your globals up top before the $(document).ready, they should be available for the page lifecycle. It can get you into trouble unless you use variable names you are sure will be unique in the document namespace, of course.

M.

Dec 28, 2012 at 11:13 AM

Yes, that global variable list at the top of code is probably my oldest lesson learned. So often, routines intended to return values did not work in my early efforts, and I discovered that writing to a global solved most such problems.

In this case--and almost certainly due to the peculiarities of asynchronous processing, the global variable trick did not work. I still have not figured out what the pattern is--why the values show up at some points of the processing but not in others, but as stated earlier I found a workaround. The essential nature of my problem is that processing my library items (=email) as a batch elicits different behavior from when the items have already been processed and are now resident in the list. If I check an item in the list--where that list was written in a previous batch, then all is well. When I write to the list as part of the current batch, then the logic that asks "is that item already in the list?" always fails. It has in fact been written to the list but is not visible as such _within the batch processing_. I therefore developed a technique that handles batch processing within arrays. Seems to work fine, so I'm satisfied.

Coordinator
Dec 28, 2012 at 5:05 PM

When you write an item into the list with UpdateListItems, the data which has been written is returned to you. Doesn't that tell you enough about what's happened? You could always push the important bits of that information into an array and refer to that.

M.

Dec 31, 2012 at 1:40 PM

I re-did that portion of the code, pushing into an array (arWritten) the items show from xData.responseXML.

global variables: arWritten (and others)

doStuff is invoked upon successful reading of email (each email represents one list entry).

Near beginning of doStuff a reference to arWritten shows empty. In the next line, we have this:
    if($.inArray(strCombo1, arWritten) > -1) {
        arCurrentDupes.push(strCombo1);
        //alert("just orphaned: " + strCombo1); //shows fine!
    }

So, although showing empty, when queried for existence of strCombo1, correctly responds to its existence (1 instance). arWritten gets populated by SPServices at end of the writeNew function. Beginning of writeNew shows the proper contents of the array, as does a display call right after insertion of its latest element.

Although arWritten is declared globally, with its contents it only shows within the function in which it is filled. Although the calling function (doStuff) cannot show those contents, we see that it programmatically exists.

All this is happening within IE7. I point this out, because an experiment several months ago showed me just how different instances of JavaScript processing can be among browsers.

Jan 2, 2013 at 3:15 PM

In my code, I open all emails in a library, then write an array containing all relevant data for each (we're mostly interested in the name as a kind of ID).

I then iterate through the array, calling doStuff, which in turn calls writeNew.

Based upon my countless failures, I proved the following. I set a global Boolean value to false. In writeNew, I set that value to true (in the completefunc section). I display the value at the beginning of each iteration of doStuff. That global value is always reported to be false. This confirms the more complicated results I've been getting with actual data: what happens in writeNew stays in writeNew, global or not.

 

Jan 4, 2013 at 7:19 PM

In general, my usual approach of using global variables has failed. Some work, some don't. Various experiments suggest a problem with JavaScript closures, a subject with which I am too unfamiliar (but will spend the weekend on). Some confirmation of this comes from Doug Crockford, who notes that IE7 did not fix the IE memory leak that does in fact affect JavaScript closures in IE. Quite possibly, this is the reason I get the "Stack overflow on line 0" message (when that message moved to line 992, I got rid of it by removing one level of SPServices), which is annoying but causes no known problem. The same code and same sample data do not generate an error under later versions of IE.

My solution--workaround, really--is to use another SharePoint list as a temporary buffer. The actions against the contents of that list work while those same actions against a global variable array do not.