Defining a variable shared by multiple asynchronous callbacks

I use the async.js library from Caolan McMahon and jQueryUI to provide user feedback, while several asynchronous calls collect data and populate the elements of a complex graph.

My question is: What is the best way for data areas to be used by asynchronous methods?

This is a simplified example of what I'm doing. I have work using global variables, but they bother me a bit and complain about jsLint. Passing arguments or defining an area in document-ready mode interrupts it.

Analogs to updateA() et. and others in my real code are hundreds of lines and include XHR calls.

JavaScript:

 // global variables. Bad? var steps = 3; var ticked = 0; var otherCounter = 0; $(function() { $('#progressbar').progressbar({ value: 0 }); async.parallel([ function(onDoneCallback) { updateA(onDoneCallback);}, function(onDoneCallback) { updateB(onDoneCallback);}, function(onDoneCallback) { updateC(onDoneCallback);} ], function(err, results) { // final callback when they're all done tickProgress('All done after ' + ticked + ' ticks.', true); }); }); function tickProgress(message) { var curvalue = $('#progressbar').progressbar('option', 'value'); var done = false; if (arguments.length > 1) { done = arguments[1]; } $('#progress_text').html(message); if (done) { $('#progressbar').progressbar('option', 'value', 100); } else { $('#progressbar').progressbar('option', 'value', curvalue + 100 / steps); } ticked++; // global OK here? } function updateA(onDoneCallback) { setTimeout(function() { $('#a').html('A is foo. otherCounter ' + otherCounter); tickProgress('updated A at otherCounter ' + otherCounter); otherCounter++; onDoneCallback(null, 'A done'); }, 1000); } function updateB(onDoneCallback) { setTimeout(function() { $('#b').html('B is bottle. otherCounter ' + otherCounter); tickProgress('updated B at otherCounter ' + otherCounter); otherCounter++; onDoneCallback(null, 'B is OK'); }, 100); } function updateC(onDoneCallback) { setTimeout(function() { $('#c').html('C is cauliflower. otherCounter ' + otherCounter); tickProgress('updated C at otherCounter ' + otherCounter); otherCounter++; onDoneCallback(null, 'C done'); }, 2000); } 

HTML:

 <p id="progress_text" style="background:yellow">Loading...</p> <div id="progressbar"></div> <hr /> <h2>a</h2> <p id="a">Looking up a...</p> <h2>b</h2> <p id="b">Looking up b...</p> <h2>c</h2> <p id="c">Looking up c...</p> 

Violins:

I have sample code in JSFiddle if you want to pounce on it.

+4
source share
3 answers

david turned me into this approach the other day in the JS chat room. See how jsfiddle works.

It uses a global object, but I like the logical encapsulation of methods inside an object better than the jAndy and kirilloid clauses .

 var Updater = (function() { var steps = 3; var ticked = 0; var otherCounter = 0; function a(onDoneCallback) { setTimeout(function() { $('#a').html('A is foo. otherCounter ' + otherCounter); tickProgress('updated A at otherCounter ' + otherCounter); otherCounter++; onDoneCallback(null, 'A done'); }, 1000); } function b(onDoneCallback) { setTimeout(function() { $('#b').html('B is bottle. otherCounter ' + otherCounter); tickProgress('updated B at otherCounter ' + otherCounter); otherCounter++; onDoneCallback(null, 'B is OK'); }, 100); } function c(onDoneCallback) { setTimeout(function() { $('#c').html('C is cauliflower. otherCounter ' + otherCounter); tickProgress('updated C at otherCounter ' + otherCounter); otherCounter++; onDoneCallback(null, 'C done'); }, 2000); } function tickProgress(message) { var curvalue = $('#progressbar').progressbar('option', 'value'); var done = false; if (arguments.length > 1) { done = arguments[1]; } $('#progress_text').html(message); if (done) { $('#progressbar').progressbar('option', 'value', 100); } else { $('#progressbar').progressbar('option', 'value', curvalue + 100 / Updater.getSteps()); } Updater.tick(); // global OK here? } return { a: a, b: b, c: c, tickProgress: tickProgress, tick: function() { ticked++; }, getTicks: function() { return ticked; }, getSteps: function() { return steps; } }; }()); $(function() { $('#progressbar').progressbar({ value: 0 }); async.parallel([ function(onDoneCallback) { Updater.a(onDoneCallback);}, function(onDoneCallback) { Updater.b(onDoneCallback);}, function(onDoneCallback) { Updater.c(onDoneCallback);} ], function(err, results) { // final callback when they're all done Updater.tickProgress('All done after ' + Updater.getTicks() + ' ticks.', true); }); }); 
+2
source

In general, it is always a good idea to create your own private Function-Context'ed "region". This can be done by wrapping the anonymous function that calls you in the application. It might look like

 (function(window, document, $) { // all your app logic goes into here var steps = 3; var ticked = 0; var otherCounter = 0; // ... }(this, this.document, jQuery)) 

This way you never compress the global namespace. Of course, sometimes you need a global object, but you really should try to avoid this, if absolutely necessary.

+5
source

You can put global vars in a state object and pass it to all callback functions. eg.

 $(function() { var progressState = { steps: 3, ticked: 0, otherCounter: 0 }; $('#progressbar').progressbar({value: 0}); async.parallel( [ function(onDoneCallback) {updateA(onDoneCallback, progressState);}, function(onDoneCallback) {updateB(onDoneCallback, progressState);}, function(onDoneCallback) {updateC(onDoneCallback, progressState);} ], function(err, results) { // final callback when they're all done tickProgress('All done after ' + progressState.ticked + ' ticks.', true, progressState); } ); }); 

Then you changed updateA, updateB, updateC and tickProgress to use the provided state object instead of global vars.

+3
source

Source: https://habr.com/ru/post/1338787/


All Articles