简体   繁体   中英

Debugging JavaScript with asynchronous functions in third-party libraries?

I'm working with https://github.com/sameer-shelavale/blitz-gmap-editor ; the file test5.html there allows you to place a marker, and when you click on it, you get a form to edit its properties:

闪电战

I've started by adding some extra buttons, that add markers with pre-populated data. Then, I click on them so the form is shown, I edit nothing in the form, and press Cancel. Approx half a second to a second after this, I get in browser console:

TypeError: a.url.substr is not a function  util.js:202:114

I would very much like to get rid of this error (note it doesn't occur in the original test5.html ).

The problem is - first I have no idea what this function is or does; I found Do we have a Uncompressed javascript Library for Google Maps API V3 :

There is no uncompressed library. All the API methods are accessed according to the published documentation, so there doesn't need to be an uncompressed version.

Yeah, right - absolutely no need for that, sure! Surely it is not Google keeping a leash on their properties - it's just that nobody would ever need to know what is going on, as it would be impossible to ever get into a problem that bad :)

Anyways, the stack traces that Firefox spits out are:

rz.prototype.load http://maps.googleapis.com/maps-api-v3/api/js/26/17/util.js:202:114
qz.prototype.load http://maps.googleapis.com/maps-api-v3/api/js/26/17/util.js:201:427
_.yz.prototype.load http://maps.googleapis.com/maps-api-v3/api/js/26/17/util.js:204:224
dz.prototype.load http://maps.googleapis.com/maps-api-v3/api/js/26/17/util.js:200:287
cz.prototype.load http://maps.googleapis.com/maps-api-v3/api/js/26/17/util.js:199:133
SS http://maps.googleapis.com/maps-api-v3/api/js/26/17/marker.js:5:217
WS http://maps.googleapis.com/maps-api-v3/api/js/26/17/marker.js:7:482
XS http://maps.googleapis.com/maps-api-v3/api/js/26/17/marker.js:7:116
VS.prototype.Y http://maps.googleapis.com/maps-api-v3/api/js/26/17/marker.js:35:646
_.zf.prototype.L/a.F< http://maps.googleapis.com/maps/api/js:112:4221

... and Chromium outputs this:

util.js:202 Uncaught TypeError: a.url.substr is not a function
rz.load @   util.js:202
cz.load @   util.js:199
oz  @   util.js:6
lz  @   util.js:6
kz.load @   util.js:201
qz.load @   util.js:201
_.yz.load   @   util.js:204
dz.load @   util.js:200
cz.load @   util.js:199
SS  @   marker.js:5
WS  @   marker.js:7
XS  @   marker.js:7
VS.Y    @   marker.js:35
(anonymous function)    @   js?v=3.26&libraries=drawing,geometry:112

So, apparently the error occurs in rz.prototype.load , and is being initiated by _.zf.prototype.L/aF< that is (anonymous function) ; from the Firefox info (...maps/api/js:112:4221), I extracted this for _.zf.prototype... :

...
_.zf.prototype.L=function(){
  var a=this;
  a.F||(
        a.F=window.setTimeout(
          function(){
            a.F=void 0; // <=== column:4221 here at ;
            a.Y()
          }
          ,a.ql)
       )
};
...

and this for rz.prototype (which contains util.js:202:114 ):

...
rz.prototype.load=function(a,b){
  var c=this.la;
  this.b&&"data:"!=a.url.substr(0,5)||(a=new _.ez(a.url)); // column 114 is right before a.url.substr..
  return c.load(a,function(d){
    !d&&_.m(a.crossOrigin)?c.load(new _.ez(a.url),b):b(d)
  })
};
...

So, the caller _.zf.prototype.L function does a setTimeout which eventually calls rz.prototype , which explains why this error is shown only after some time has expired after I press "Cancel".

But my question is - what is it in the rest of the code, which might call this _.zf.prototype.L function? How can I find that? If _.zf.prototype.L itself was called through a setTimeout , would it have "remembered" the originating caller/call stack, and can that be retrieved for debugging? Or, would it be possible to set a breakpoint, which breaks whenever a function with a name _.zf.prototype.L is about to be called?

I tried looking into How to view JavaScript function calls as they occur (which has no info on how to do a function call trace); and then found https://javascriptweblog.wordpress.com/2010/06/01/a-tracer-utility-in-2kb/ - using this library, I can do:

tracer.traceAll(BlitzMap,true); 
tracer.traceAll(BlitzMap.getMapObj(),true); 

.... but then it traces the object functions OK, the map obj is a bit weird (there's like 3000+ tracing traceOff messages in the console while the page loads) although it seems to trace some parts too - however, when I do my test case, after pressing "Cancel" all I get are my code traces (which do not crash), and then a second goes by, and I get the error/stacktraces above without any other trace.

So, how to debug a problem like this?

Ok, I managed to get somewhere with this problem - even if the close vote claims the problem is "too broad".

First, if the "front-facing" javascript from the API is:

<script src="http://maps.googleapis.com/maps/api/js?v=3.26&libraries=drawing,geometry" type="text/javascript"></script>

... then download it to a file:

wget "http://maps.googleapis.com/maps/api/js?v=3.26&libraries=drawing,geometry" -O gmapsapi.js

... and use this file in your HTML instead of the online one:

<script src="gmapsapi.js" type="text/javascript"></script>

Then open your HTML file in Firefox, have it run, and then do in Firefox: File / Save Page As / Web page, complete.

This will download additional scripts like util.js and such locally in the saved page folder.

Now, open the saved (as Web page, complete) version of the HTML file - it should have also replaced the online links to scripts to the downloaded local ones.

Then, open gmapsapi.js from the saved folder, and introduce linebreaks to _.zf.prototype.L function:

_.zf.prototype.L=function(){
  var a=this;
  a.F||(a.F=window.setTimeout(function(){a.F=void 0;a.Y()},a.ql))
};

... to be able to set up a breakpoint on a line later.

Now, open the saved HTML file in Firefox, open Firebug, in the Script tab find gmapsapi.js , and set a breakpoint on the var a=this; line.

Now run the stuff - in my case, doing all up to and including pressing Cancel - and the JS engine will break; then you can do console.trace() in Firebug console, and obtain this kind of a trace (after which the error occurs):

_.zf.prototype.L()                            gmapsapi.js (line 114)
VS.prototype.changed(a="modelIcon")           marker.js (line 35)
Bb(a=Object { type="object"}, b="modelIcon")  gmapsapi.js (line 37)
Bb(a=Object { type="object"}, b="icon")       gmapsapi.js (line 37)
_.k.set(a="icon", b=Object { type="object"})  gmapsapi.js (line 96)
_.vc/<(c=Object { type="object"})             gmapsapi.js (line 47)
_.k.setValues()                               gmapsapi.js (line 97)
a = Object { type="object"}
BlitzMap</this.updateOverlay()                blitz.js (line 545)
BlitzMap</this.closeInfoWindow()              blitz.js (line 514)
onclick()input[3] onclick (line 1)
event = Object { type="object"}

... and indeed - this pointed out where the bug is: namely, I had hacked blitz.js and ended up writing an empty string "" for the icon of a marker, which ended up being the cause of the error.

This stack trace reveals one more thing - and that is, why does this error occur only the first time I press cancel, and not subsequent times; well, the answer is, the erring code runs only when VS.prototype.changed(a="modelIcon") , so the second time, the setting of empty string is not a "change" anymore, so no error then.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM