"Da is' was kaputt!"
debugging client-side JavaScript
slides in english, talk in german
agenda
debugging javascript
the debuggable application
first part: debugging javascript with chrome dev tools
second part: the debuggable application
history
how did we get here?
- come to work
- svn up, git pull, composer install, bower install, vagrant up, go nuts
- get coffee
open your dev environment and ..."HUHU"
- annoying in staging env
- embarassing in production env
debugging javascript
✗ alert()
✗ document.write() | $.fn.html() | $.fn.text()
✗ console.(debug|log|warn|error|trace)()
only information: yes a certain part of the code was executed
But also the console can be tricky under certain circumstances [click]
possibility to debug objectsBUT also some caveats
[code]
+ still no information about the execution context
debugging javascript
✗ alert()
✗ document.write() | $.fn.html() | $.fn.text()
✗ console.(debug|log|warn|error|trace)()
Conclusion:
There has to be a better way to debug!
debugging JavaScript
in 2014
✓ Breakpoints & the debugger; statement
✓ Command Line API
✓ Source Maps
and lucky for us there is!
demo
debugging JavaScript in 2014
"The javascript doesn't work in IE7"
- Quality assurance
- no DevTools in IE
- no good DevTools in IE
"Something's fucky broken"
- Customer
- Debugging isn't easy
- especially with minified / recomplides code
TypeError: e is undefined
main.js
line 1
- I guess everybody has seen this before...
- Follow the link to the javascript console and you'll see...
- this! (enterjs homepage)
- or more likely... [click]
- this! (no syntax highlighting)
- btw: this is what pricesearch javascript looks like in prod
- Source Maps
- afaik Chrome Canary, Cromium and Firefox only
- only with closure compiler
the debuggable application
how can my application help me debug itself?
watch your stacks!
- most important: meaningful Stacktraces
- sadly neglected too often
- but they can be really helpful
- so: how do we get them?
{object Error}
- important to understand how error handling work in the browser
new Error([message[, fileName[, lineNumber]]])
message, name, description, number, fileName, lineNumber, columnNumber, stack, toSource(), toString()
- Error object definition taken from MDN
- constructor: message, fileName, lineNumber
- props: z.B. message, name, description, stack
- great, right? Why am I even bothering making this presentation?
but be aware
and this is just the beginning ...
- because it's not that easy [click]
- many incompatibilites (browsers):
- fileName not standardized
- lineNumber not standardized
- IE has differnt implementation of failing with every version
- Chrome sets line prop., Firefox lineNumber
- Prop of an instance vs. Prop. of a prototype
- Different formats for stacktraces (example)
demo
{object Error}
error.html
compares stacktraces accross browsers
(+ thrown vs. unthrown)
Error bubbling
the path of the Error
-
[native code]
-
function foo()
-
function bar()
- native code (javascript runtime)
- [click] executes function foo()
- [click] executes function bar()
-
[native code]
-
function foo()
-
function bar()
Error in function bar()
-
[native code]
-
function foo()
-
function bar()
Error bubbles up through function foo()
-
[native code]
-
function foo()
-
function bar()
All the way back to javascript runtime
-
?
-
[native code]
-
function foo()
-
function bar()
And what does the javascript runtime do?
-
window.onerror
-
[native code]
-
function foo()
-
function bar()
delegates to window.onerrorIn general not a bad thing
Websites should not crash the browser process
-> hence: global default error handler
demo
error bubbling
onerror.html
sync.html
demonstrate global error handler
(stacktraces go missing)
error bubbling
conclusion
✓ catch your errors before window.onerror
✓ name your anonymous functions(careful in older IEs, though)
error bubbling
the way of the error - async edition
even more fun, when we start crossing the "asynchronous boundary"
-
function foo()
-
[native code]
asynchronous boundary
-
function bar()
- again function foo() [click]
- execute native code, say windowSetTimeout or addEventListener [click]
- so: registers asynchronous callback [click]
- at a later point...
-
function foo()
-
[native code]
asynchronous boundary
-
function bar()
Callback executes and causes an error
-
function foo()
-
[native code]
asynchronous boundary
-
function bar()
Error propagates back to the javascript runtime
-
function foo()
-
[native code]
asynchronous boundary
-
function bar()
Error will be propagated to function foo(), right?
One could think so because this is where addEventListener/setTimeout was called
-
window.onerror
-
function foo()
-
[native code]
asynchronous boundary
-
function bar()
wrong!
java runtime does not know how to handle this error
because of new execution context
==> Sample
demo
error bubbling - async edition
async.html
demonstrate global error handler in async situations
(catch errors early!)
Stacktraces aren't everything
what was the user doing?
what state was my application in?
what's the environment?
- which 'Page' did the user navigate to?
- which Payload was sent to the client?
- Browser extensions?
debug-friendly software
example: Mediator Pattern
- trivago: messaging pattern
- Framework based on Marionette
- top level mediator
- comparable to flux
tooling
e.g. {Track:js}
also, there are a lot of tools to aid in debugging
especially when it comes to error context tracking
{Track:js} is one example
tooling
other
debug mode (e.g. via Cookie)
browser extension
- e.g. cookie to boot into debug session in prod environment
- browser extension for common, domain specific tasks
the debuggable application
conclusion
✓ know your {object Error}
✓ handle your errors early
✓ name your anonymous functions
✓ log everything / get error context
- understand error handling in javascript
- handle errors in time
- name your anonymous functions => meaningful Stacktraces
- Debug friendly application (MessageBus/Mediator)
- tooling: {Track:js}