Help needed with the JavaScript / OOP variable scope and callback functions

I think this problem goes beyond typical variables and closures, or maybe I'm an idiot. It's all the same here ...

I create a bunch of objects on the fly in the jQuery plugin. The object looks something like this.

function WedgePath(canvas){ this.targetCanvas = canvas; this.label; this.logLabel = function(){ console.log(this.label) } } 

The jQuery plugin looks something like this:

 (function($) { $.fn.myPlugin = function() { return $(this).each(function() { // Create Wedge Objects for(var i = 1; i <= 30; i++){ var newWedge = new WedgePath(canvas); newWedge.label = "my_wedge_"+i; globalFunction(i, newWedge]); } }); } })(jQuery); 

So ... the plugin creates a bunch of wedgeObjects, and then calls a global function for each of them, passing in the last instance of WedgePath. The global function looks like this.

 function globalFunction(indicator_id, pWedge){ var targetWedge = pWedge; targetWedge.logLabel(); } 

What happens next is that the console registers each wedge mark correctly. However, I need a little more complicated in globalFunction. So actually it looks like this ...

 function globalFunction(indicator_id, pWedge){ var targetWedge = pWedge; someSql = "SELECT * FROM myTable WHERE id = ?"; dbInterface.executeSql(someSql, [indicator_id], function(transaction, result){ targetWedge.logLabel(); }) } 

There is a lot going on, so I’ll explain. I use a client-side database repository (this is called WebSQL). The 'dbInterface' instance of a simple javascript object I created that handles the basics of interacting with a client database [shown at the end of this question]. The executeSql method takes up to 4 arguments

  • SQL string
  • optional array of arguments
  • optional onSuccess handler
  • optional onError handler (not used in this example)

I need this to happen: When a WebSQL query completes, it takes some of this data and manipulates some attributes of a particular wedge. But when I call logLabel on the WedgePath instance inside the onSuccess handler, I get a shortcut to the most recent WedgePath instance that was created back in the plugin code.

Now I suspect the problem is var newWedge = new WedgePath(canvas) ; line. So I tried to push each newWedge to an array, which I thought would prevent this line from replacing or overwriting the WedgePath instance at each iteration ...

 wedgeArray = []; // Inside the plugin... for(var i = 1; i <= 30; i++){ var newWedge = new WedgePath(canvas); newWedge.label = "my_wedge_"+i; wedgeArray.push(newWedge); } for(var i = 0; i < wedgeArray.length; i++){ wedgeArray[i].logLabel() } 

But then again, I am creating the last instance of WedgePath.

It drives me crazy. I apologize for the question, but I wanted to be as clear as possible.

END

==================================================== =============

In addition, the code for the dbInterface object should be appropriate here.

 function DatabaseInterface(db){ var DB = db; this.sql = function(sql, arr, pSuccessHandler, pErrorHandler){ successHandler = (pSuccessHandler) ? pSuccessHandler : this.defaultSuccessHandler; errorHandler = (pErrorHandler) ? pErrorHandler : this.defaultErrorHandler; DB.transaction(function(tx){ if(!arr || arr.length == 0){ tx.executeSql(sql, [], successHandler, errorHandler); }else{ tx.executeSql(sql,arr, successHandler, errorHandler) } }); } // ---------------------------------------------------------------- // A Default Error Handler // ---------------------------------------------------------------- this.defaultErrorHandler = function(transaction, error){ // error.message is a human-readable string. // error.code is a numeric error code console.log('WebSQL Error: '+error.message+' (Code '+error.code+')'); // Handle errors here var we_think_this_error_is_fatal = true; if (we_think_this_error_is_fatal) return true; return false; } // ---------------------------------------------------------------- // A Default Success Handler // This doesn't do anything except log a success message // ---------------------------------------------------------------- this.defaultSuccessHandler = function(transaction, results) { console.log("WebSQL Success. Default success handler. No action taken."); } } 
+4
source share
2 answers

I would suggest that this is because the client-side database repository works asynchronously, like an AJAX call. This means that it does not stop the call chain in order to wait for the result from the called method.

As a result, the javascript engine completes the for loop before running globalFunction.

To get around this, you can execute the db request in close.

 function getDataForIndicatorAndRegion(indicator_id, region_id, pWedge){ return function (targetWedge) { someSql = "SELECT dataRows.status FROM dataRows WHERE indicator_id = ? AND region_id = ?"; dbInterface.sql(someSql, [indicator_id, region_id], function(transaction, result) { targetWedge.changeColor(randomHex()); }); }(pWedge); } 

This way you save pWedge for every execution. Since the second method calls it itself and sends what pWedge is an argument right now.

EDIT: Updated code from comments. And made changes to it. The callback function may not need to be called on its own. If he calls it himself, the result of the function is passed as an argument. Also, if that doesn't work, try passing other arguments.

+3
source

I suspect your problem is with a modified closure inside globalFunction:

 function(transaction, result){ targetWedge.logLabel(); }) 

read this

0
source

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


All Articles