A Heaping Stack of Scripts
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Alternative Title |
| |
Title of Series | ||
Number of Parts | 170 | |
Author | ||
License | CC Attribution - NonCommercial - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/50820 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
00:00
Stack (abstract data type)Error messageData typeParsingMultiplication signCartesian coordinate systemStack (abstract data type)BuildingOpen sourceProjective planeScripting languageProcess (computing)Tracing (software)Memory managementComputer animation
01:44
Error messageMessage passingStack (abstract data type)Error messageProduct (business)Cartesian coordinate systemJava appletScripting languageSoftware bugMessage passingOperator (mathematics)Process (computing)Staff (military)Right angleGoodness of fitFormal languageInformationProgrammer (hardware)State of matterDescriptive statisticsObject (grammar)QuicksortSynchronizationKeyboard shortcutComputer fileChainMeeting/InterviewComputer animation
03:42
File formatStandard deviationContent (media)Internet ExplorerRevision controlInternetworkingConsistencyError messageStaff (military)Different (Kate Ryan album)State of matterAsynchronous Transfer ModeProcess (computing)InternetworkingRevision controlObject (grammar)Web browserComputer fileError messageException handlingSource codeComputer animationMeeting/Interview
04:28
Error messageDemo (music)Object (grammar)Message passingDegree (graph theory)MIDIDemo (music)InformationForm (programming)Process (computing)Computer fileTracing (software)Object (grammar)Different (Kate Ryan album)Web-DesignerNumberRight angleString (computer science)Web browserGraphical user interfaceStandard deviationSpacetimeCategory of beingLetterpress printingElement (mathematics)ImplementationRevision controlSystem callPoint (geometry)InternetworkingNetwork topologyData structureSoftware frameworkWeb 2.0Error messageLine (geometry)File formatReading (process)Computer animation
07:11
Error messageMenu (computing)Uniform resource nameObject (grammar)Maxima and minimaCodeStack (abstract data type)Error messageSimilarity (geometry)ImplementationProjective planeMereologyLine (geometry)Software development kitNumberSystem callGodInformationStaff (military)Revision controlObject (grammar)Network topologyDifferent (Kate Ryan album)Open sourcePoint (geometry)Web browserData structureState observerGraphical user interfaceFile formatDemo (music)Web 2.0Library (computing)Process (computing)Stack (abstract data type)InternetworkingTracing (software)Computer animation
10:06
Error messageObject (grammar)Message passingFLOPSMenu (computing)AdditionMarkup languageProjective planeIntegrated development environmentWeb browserRevision controlTracing (software)Letterpress printingError messageFunctional (mathematics)Stack (abstract data type)Open sourceInformationAlgorithmFamilyBit rateNetwork topologyPRINCE2Software development kitQuantum stateString (computer science)Data structure
12:19
Error messageMenu (computing)SimulationObject (grammar)SequenceString (computer science)Buffer overflowError messageStack (abstract data type)Web browserException handlingInformationCodeMultiplication signSoftware developerDemosceneObject (grammar)Right angleArmProcess (computing)Functional (mathematics)WindowFamilyMereologyDifferent (Kate Ryan album)Computer animation
13:26
Error messageParameter (computer programming)WindowInformationGauß-FehlerintegralObject (grammar)Exception handlingFunctional (mathematics)MereologyError messageWeb browserCode
13:56
Error messageCodeSimultaneous localization and mappingDemo (music)Function (mathematics)Hardy spaceNumberScripting languageMultiplication signType theoryInformationInternetworkingCartesian coordinate systemException handlingService (economics)Game theoryProcess (computing)Graphical user interfaceSoftware developerPlanningMachine codeWeb browserCategory of beingHill differential equationCodeWeb applicationLogic gateCrash (computing)Tracing (software)Demo (music)Algebraic closureFunctional (mathematics)Point (geometry)MathematicsFormal languageBitSystem callObject (grammar)Office suiteParameter (computer programming)Numbering schemeLine (geometry)Thread (computing)Error messageComputer fileChainGauß-FehlerintegralRight angleWeb 2.0WindowTypprüfungComputer animation
18:03
Error messageInterior (topology)Function (mathematics)Message passingSample (statistics)Hardy spaceFunctional (mathematics)Error messageIterationPoint (geometry)Machine codeLevel (video gaming)WindowChainElement (mathematics)Single-precision floating-point formatStaff (military)Tracing (software)Network topologyComputer animation
19:26
NumberFunction (mathematics)Error messageMessage passingStack (abstract data type)SynchronizationComputer fontEvent horizonMobile appChi-squared distributionSoftware bugInternetworkingObject (grammar)CodeRule of inferenceFunctional (mathematics)ThumbnailInformationRevision controlError messageLine (geometry)Hand fanMultiplication signSystem callSpherical capBitKeyboard shortcutDemo (music)Inheritance (object-oriented programming)CASE <Informatik>Scripting languageCartesian coordinate systemSurgeryWindowMultilaterationImplementationAddress spaceEmailProcess (computing)MereologyDecision theoryRight angleBlock (periodic table)Descriptive statisticsEvent horizonAlgebraic closureHardy spaceReal numberMachine codeSoftware developerIterationEncapsulation (object-oriented programming)Server (computing)Mobile appSoftware bugFormal languageComputer animation
25:14
Error messageMobile appDemo (music)Multiplication signMachine codePointer (computer programming)InformationException handlingCartesian coordinate systemCodeBitContext awarenessEvent horizonWordComputer animation
26:06
Mobile appScripting languageRouter (computing)Error messageNumberMessage passingStack (abstract data type)Functional (mathematics)Event horizonSystem callPatch (Unix)Punched cardMultiplication signDifferent (Kate Ryan album)CASE <Informatik>Machine codeAdditionProcess (computing)Cartesian coordinate systemAlgebraic closureCategory of beingRight angleCodeBitNetwork topologyWordError messageComputer fileGauß-FehlerintegralException handling
28:59
Error messageBoundary value problemStack (abstract data type)Event horizonMultilaterationParameter (computer programming)Multiplication signError messagePunched cardKeyboard shortcutStaff (military)Network topologyFunctional (mathematics)Statute
29:58
Error messageMessage passingStack (abstract data type)NumberSynchronizationExecution unitPatch (Unix)Hacker (term)Boundary value problemAbstractionLevel (video gaming)Operator (mathematics)Functional (mathematics)Algebraic closureType theoryWindowChainSystem callEvent horizonMachine code2 (number)Set (mathematics)CASE <Informatik>Punched cardStructural loadProjective planeImplementationSingle-precision floating-point formatLimit (category theory)Web browserMultiplication signContext awarenessGraph coloringCartesian coordinate systemRevision controlNumberObject (grammar)Shortest path problemComputer fileProper mapCodeExistenceUniform resource locatorFiber bundleProcess (computing)Line (geometry)Message passingError messageMathematicsHeegaard splittingScaling (geometry)BitComputer animation
34:41
Error messageSynchronizationStack (abstract data type)Message passingSimultaneous localization and mappingMaxima and minimaCodeNumberWeb browserGame controllerObject (grammar)ImplementationDemosceneError messageComputer animation
35:15
Error messageMessage passingNumberStack (abstract data type)Demo (music)Coma BerenicesWeb applicationSoftware developerWeb 2.0Computer animation
35:53
Query languageFunction (mathematics)Event horizonIntegrated development environmentMotion captureContext awarenessComputer wormError messageContinuous trackGraphical user interfaceNetwork topologyInformationTrailContext awarenessExtension (kinesiology)Cartesian coordinate systemMereologyMultiplication signSoftwareDescriptive statisticsProcess (computing)Hacker (term)Web pageIntegrated development environmentMetropolitan area networkProjective planeState of matterQuicksortBit rateWeb browserComputer clusterCAN busCodeOrder (biology)System callTracing (software)Error messageComputer animation
Transcript: English(auto-generated)
00:03
Hi everybody. Oh, hi everybody. I don't need to talk quite so loud. Thanks everybody for taking some time to come talk to me. My name is Todd Gardner. I'm from Minnesota. This is my first time in Oslo, my first time at NDC, and it's amazing. Seriously, like when I got here about a week ago now, and I didn't know anybody here,
00:25
and since then I'm actually looking across and I see like a lot of familiar faces that like we've become friends for the last couple of days, and that's amazing. So I'm from America, and in America, friends take selfies. So would you guys take a selfie with me? That's awesome. Thank you.
00:48
So this talk is called A Heaping Stack of Scripts, which is about how to get stack traces in a JavaScript application, and then how to get better stack traces that can actually help you do something
01:00
and understand how your application is breaking. Because you're building something truly amazing. I know you are. I've talked to a lot of you about the applications that you're building either for your customers or for just your side project or for your open source thing, and I'm truly awestruck on like some of the things that are going on here, but as amazing as anything that we're building is,
01:23
that turns to crap really fast when it blows up in your face. It's even worse when your customer or your user is really liking your application, and then it breaks in some really frustrating way, and quickly all of that joy that they had using your tools
01:44
turns to such frustrating anger because this unicorn of a product that they thought existed, now they feel is buggy and that they can't use it. And that makes the customers angry, and it makes me as the developer angry because all of the hard work that I put into my application just totally disappeared.
02:04
And so we go to debug our applications to like get rid of these things, and rather than finding errors that are useful in JavaScript, typically we find errors that look like this, that tell you almost nothing about what actually went wrong, how to fix it, how the user got into this state.
02:21
I have no idea where to even begin. So what I want to talk about today is I want to talk about the JavaScript error object itself and how it was implemented and how it behaves. And then I want to talk about the paths that errors take through our applications and how we collect information about them. And then I want to talk about some common misconceptions with asynchronous errors
02:46
because JavaScript is an asynchronous language. We bind up events, and then they may get called later, and so we don't always understand how our errors will bubble back through those asynchronous chains. And then I'm going to totally destroy some JavaScript in front of you
03:00
and do things that most good programmers would tell you to never, ever do. But we're going to play with it a little bit and try and do some useful things. Sound good? All right. So we're going to start with the docs. The JavaScript docs are MDN. Don't ever use W3 schools. Use MDN.
03:20
On MDN, here's the error. And it looks fairly straightforward. There's nothing particularly magical here. We can construct it with a new operator. We get all kinds of good things that we would expect from any other language. The message, file name, et cetera. And the stack trace. That's just there. We're supposed to just get it for free, right? So what's the big problem?
03:41
Well, my buddy Spock does some JavaScript. And he doesn't believe any of that because that is not true at all. Because the reality of an error is that none of that works everywhere. The file name, line, and column are not consistent in any browser that you'll use even today. Different versions of Internet Explorer and different document modes of Internet Explorer
04:02
will behave differently when it comes to the error. Every single one of them has its own quirk. And the stack trace itself is not standard at all. And there's so many more footnotes if you dig into the error object itself to understand why it's, how they're different. And in the end, we're just left with the Internet is broken and nobody cares.
04:22
Because how is error handling at this state of affairs in JavaScript at this point? And so we just feel like George Costanza. And it's just terrible. So let's play with it. I'm going to do a lot of demos. Can everybody read that?
04:41
Can anybody not read that? Good? All right. Thank you. So I'm going to do a lot of demos today. All my demos are fairly straightforward. Just simple HTML documents with some JavaScript slammed into it. Not a whole lot going on.
05:00
I write everything here in vanilla JS. I'm not using any significant web framework. The only magic that I've written is this little property called print props, which you'll see in my demos. What print props lets me do is it lets me take an object and render it inside of an element. Just so we can look at how different objects look in different browsers.
05:21
And that's actually not the demo I want to start with. That's the demo I want to start with. So let's start with an error. So in this demo, I'm just going to do the simplest possible thing. I'm going to create an error object and I'm going to see what it looks like. So if we run that, here in Firefox, here's the error.
05:47
And it looks pretty close to what MDN told us it would be. Column number is zero. That's kind of weird. I don't think it was on the zero column, but everything else is there. And it has a stack trace, and that stack trace has some information. So that's pretty cool.
06:00
But if we compare it to Chrome, which I was not on the right one. If we compare it to Chrome, already it's different. So latest Firefox, latest Chrome, arguably kind of the standards for a lot of web development. And the implementation is already different.
06:21
Chrome does not have the column number, file name, or line number on the main error object. But there they are. They're kind of like embedded in the stack trace. But that stack trace is even a different format. Those are just strings with like various different white space characters and decorations around it. So you couldn't just parse a stack trace and reason about it unless you also knew what browser it came from.
06:45
We also look at this in Internet Explorer where it is different again. And this is Internet Explorer 11. All the browsers that I'm showing today are the latest and greatest. Because showing things in old versions of all of these browsers just makes me too depressed to get up here and talk about.
07:03
But in Internet Explorer 11, the error object doesn't really have anything meaningful on it at all at this point. Well, so maybe we should try throwing an error. Throw is, you know, that's typically what we do with an error. Very rarely do we just create one for no particular reason. So let's try throwing an error.
07:21
And so here comes the live coding part. So I hope the demo gods are smiling upon me. So we're going to just throw a new error and we are going to catch it.
07:43
And we will print it out what we caught into a, let's put this somewhere else. Let's put this in JS error thrown. And we'll just make a little home for it.
08:04
So now we can take a look at this again. And we can compare a thrown error and an unthrown error. Well in Firefox, they look pretty close. But now column number actually has a value in it. Which is kind of weird. The act of throwing seems to be what populates a column number in Firefox.
08:24
As an observer of their API, I would have imagined that line number and column number would have a similar implementation and similar behavior. Because they are giving me similar kinds of data. So that's a little weird. If we take a look at Chrome, it looks entirely sane.
08:41
Apparently the act of throwing in Chrome does absolutely nothing at all to the object. They look identical. And if we take a look at Internet Explorer, bam! Now we actually have meaningful information. So apparently in Internet Explorer 11, an error is not really anything at all until it's thrown.
09:01
But at that point we do get a stack trace, which does have some meaningful information. But if you'll notice, yet again we have a third format of stack traces. It does not look like Firefox's. You know I'd never noticed that before, but that's really interesting.
09:23
I have no idea why it's different. That's great. We should figure that out afterwards. But the stack trace itself, you'll see that it's a different format than either Firefox or Chrome. And so we have yet a third kind, a third format of stack traces to parse.
09:41
And if you expand this and start looking at other browsers, start looking at other implementations of WebKit, look at different versions of Opera, old versions of these browsers, their formats will change. And so reasoning about a stack trace is very important in JavaScript because it's where almost all of our information comes from, but it's very difficult to do because they're all in a slightly different format.
10:03
So I want to talk about a little library that's out there that's very cool, briefly. It's called Stack Trace JS, and it's an open source project out on GitHub. And so it's trying to solve some of these problems. So if we just include that here in our little demo, and we'll give this another try. So I'm going to add another attempt here at an error, a stack trace error.
10:31
Stack trace. What Stack Trace JS introduces is it introduces a global function called print stack trace.
10:40
And what print stack trace does is it attempts to do all kinds of really clever things to guess what a stack trace is based on the browser and the current environment. And then it normalizes that stack trace unless I screw it up.
11:03
You're right, I did miss the markup. Thank you. See, you guys have my back. I love that about you.
11:23
So now if we, oh, now I'm not even printing the right thing. So print stack trace will generate a stack, a normalized stack that you could do other things with. For what I'm going to do is I'm just going to stick it right back on the air so that it looks normal. And so now what I get with a stack trace JS version of the error is I get not just a set
11:42
of strings, but I actually get an array of strings, things that I could actually programmatically enumerate across and do things with. And if I look at it in different browsers, now they are still not 100 % the same because not all the browsers have all the information, but they're closer. I could actually run these things through a similar algorithm and be able to reason about what went wrong.
12:06
Stack trace JS is very cool. I encourage you to look at it. There's a similar project called trace kit, which does some additional things with function wrapping. But they're both very, very neat projects.
12:20
I want to talk about some other things that developers tend to do in JavaScript when it comes to errors. Because a lot of times you'll see an example in an old jQuery example on stack overflow where people throw anything they want. Because it's JavaScript, right? You can just do whatever you want.
12:41
I could just throw, you know, something bad happened. Because I can just throw a string. I don't have to throw errors, right? But when you do that, where did I do that? Oh, I didn't save it. JS error thrown. When I do that, it's not particularly valuable.
13:03
Like really you're just exercising another path through your code through the try catch sequence that's very performance expensive to do. But then you don't even get any good information. You don't have anything at all. The browsers are remarkably consistent about not giving you anything at all.
13:22
But I still don't think it's a good thing to do. The other part of the base JavaScript error object in browsers is the window on error function. So this is implemented in all browsers that you'll care to interact with. But it's a little different in all of them.
13:41
So I want to play with that. So if you attach a function to window on error, any unhandled exceptions that come out of your code, the browser will kindly try and pass there to you. And you can get some information. But if we take a look at what we get in Firefox, I'm printing the arguments array.
14:08
And so I'm printing out that we get three things. Well, four things if you really care about counting how many things you got. But you get three things. You get the name of the error, the file it came from, and a line number. That sounds a lot like type error is undefined scripts JS line one.
14:22
Because that, in a minified piece of code, will not actually tell me very much about what things are happening. But if we go to, say, Internet Explorer and take a look at that, something amazing has happened here.
14:41
Something truly remarkable. Internet Explorer is giving us not just a little bit more information than Firefox, but way more information than Firefox. This fourth property that we're getting passed in is the actual error object passed into your global error handler. Chrome has this as well. Chrome actually beat Internet Explorer out the gate with this.
15:03
But being that this is kind of a Microsoft heavy conference, I wanted to give IE some kudos because that's awesome. That's way better than what you get with Firefox over here. But this is both amazing and terrible. It's amazing in that I now have access only in, like, the very latest browsers.
15:23
I have access to the stack trace from anywhere in my application. I just have to attach this one global error handler and I get so much information. But it's also terrible because we have been developing web browsers that run JavaScript for a really long time. And this is the first time we're starting to think about, hey, maybe we should actually pass the error to the global error handler.
15:47
It's only now occurring to us to do this. Yeah, so that's what I want to talk about. So that was my first demo.
16:01
I like that one. It's a little demo that I like to call the game of throws. The future looks amazing. I'm so excited for a time when we can just attach to this global handler and get all of this information. But right now, chances are we have to deal with all kinds of browsers that probably don't support any of this.
16:26
Probably we all still have to support, I mean, I'm sure we all have to support IE 10. It's not very old. Probably 9, 8, 6, 5, 5.
16:47
Man, how deep into that rabbit hole do you want to go? Netscape Navigator, yes. All right, so that's errors. So now let's talk about error paths. So this is a typical demonstration of what your application will do.
17:05
So at some point, the native code, the browser decides, hey, I want to start your function. I'm going to call your main closure. And it calls foo, which calls bar, which calls baz. And then at some point in execution, something terrible happens. And an exception is thrown. And it bounces back up that call chain just like it would in any other language that I'm sure you're used to.
17:24
But then it lands back on the native code. And we don't really want an unhandled exception to, like, crash the native code. In a server-side application, I heard a bunch of people argue that, you know, an exception should crash the main thread. Well, the main thread is Chrome or Firefox or Internet Explorer. I don't think anybody wants to allow us to build applications where we can crash the web browser.
17:45
Generally, that's not a good idea. So instead, what does the native code do? It passes it back into JavaScript across this window on error function to try and give us, as the developers of this web application, an opportunity to deal with it.
18:01
So let's take a look at that quick. This should be a fairly trivial example. If we take a look at this example, I have a simple JavaScript function here, a couple of functions that I'm going to call through. I have a function that I've labeled the outer function. And the outer function will call this thing, this iterator across each of a single element of array of fruits and blow up.
18:27
Because apparently it does not like apples. And it will just print it out. And on each stage of this execution, I'm capturing what does the error look like and letting it bubble back out. All the way out to window on error.
18:41
Sound good? So if we take a look at what this actually runs as, we see that if we, this full stack trace at the point of the inner function, at the point the error happened, we actually know the full execution chain. So we don't need to, like, allow the error to recurse back, which is I'm sure obvious, but I just want to point that out.
19:01
So we see the full error here in the inner function. We see it passed out identically to the outer function. The outer function doesn't have anywhere to go, so it passes to the native code. We wind it back on window on error with nothing. And that's kind of terrible.
19:21
So if we look at this stack trace, can you all see the stack trace? Let me zoom in a little bit more on that. Maybe a little less. So if we look at that stack trace, it's kind of weird. It doesn't tell us a whole lot about what's actually going on there.
19:41
We see outer function and then some crazy characters that don't mean anything. And then another outer function, and then some anonymous thing, and then some anonymous thing. So all this is really telling me is that somewhere in my code base there's something called outer function, and that has something to do with this error. And that's really all it's told me. But it could tell us so much more.
20:03
We could name some of our other functions. So, for example, if I go in here and we take a look at this, I imagine that here this line 39 is right here. It's where we're throwing it inside of this inner function. Well, why don't we just give this a name? We could call it like the fruit iterator function.
20:24
And now if we run this, now we have some better information about it. I actually have a name attached to each line in the stack trace. The outer function is already named, but if I go out to line 56, 56 is where we're invoking outer function, and we're invoking that from my closure here. So I could even name my closure and call this my main closure function.
20:48
And now I get a lot more information. So this is really cool. You can name your functions today in JavaScript and you get a ton more contextual information. However, I do want to point out there are some not quite bugs,
21:02
but implementation decisions that some old versions of Internet Explorer decided to make. Where the act of naming a function here exposes that as an object in its parent scope. And so, for example, if I was to do something like, if I was to create an object, and then I was going
21:25
to put a function on that object, and I name that function foo, there are two foos that would actually have gotten created. There's the one sitting on this object. There's also just a foo out here that Internet Explorer would have created because it leaked it out into its global scope.
21:43
And there's a ton of articles about this that I can tweet out later if you're interested that go in very in-depth in it. A handy just rule of thumb is to just prefix the naming here, which I was doing before, just add FN. Just to prevent those global conflicts from happening. You should give something a name that's different from what you're assigning it to,
22:02
but it's descriptive so that you get information for your stack trace about it. So that's just a handy little tip. Name your functions, but don't name them the same as the variable you're assigning them to. So I think that's all I want to do with that.
22:20
But that was all pretty trivial, right? That was the same kind of errors that happened in any server-side language that you might be using. The interesting thing about JavaScript and the part that trips a lot of us up are callbacks. Because when a callback happens, it's some code that we wrote that's calling back into the native code.
22:41
I'm calling add event listener because I want to respond when the user clicks on a button. Or I'm calling window.setTimeout because I want to do something later. And then the native code says, great, thanks, that's awesome, I'll get back to you. When that event happens, it calls your function for you. But what happens when that function blows up?
23:02
What happens when you didn't properly implement the button handler? Or you got an Ajax back from your server that you were not expecting to get? Well, what many of us will do is we'll write some code expecting that, hey, foo will catch that error for me, right? Because foo is the encapsulation of that. But the time of that error, foo doesn't exist anymore.
23:22
It is no longer executing. And that error just goes straight back to window on error, and it's very confusing. So I want to show you that. So if we look here at, I don't want to save that.
23:41
If we look here, I'm going to use this demo for a while because now we're starting to look a little bit like an application. I've created an application object. And my application object initializes. It binds up a button, and when the user clicks on a button, I call my click handler, which I've ingeniously named onClick.
24:01
And onClick I improperly implemented to throw an error rather than doing something real. But I want to be a good developer. And so my application, I want to do some cool things when my application dies. I want to pop a modal to my customer and say, hey, I'm sorry, give me your email address and I'll shoot you a T-shirt and we'll fix it in a week or whatever.
24:23
I want to do some interesting things. So I've implemented my own on-error handler. And because I want to catch everything, I just surround my entire application blocks. I want to try all of the things and I want to catch all of the things and pipe all of those through to my error handler.
24:40
And then just in case, for the purpose of this demo, we'll throw in a global handler as well. And I kick off the application and initialize it. So if we go take a look at what this looks like, here's my app. It doesn't do anything until you click a button.
25:00
And I generate an error. And I totally don't do what I expected. I wanted to catch it in my application error handler. I wanted to do interesting things with it. But it didn't. Because I passed this on click handler. I gave it to the native code.
25:21
At the time when this click handler is executing, it's not in application anymore. It was a pointer. It was a reference back from the native code. And so it didn't do what I expected. It doesn't do what a lot of people expect. Because I've seen a lot of try catches like this where you don't get the information that you want and you just end up on this global error handler.
25:45
So now I want to talk a little bit about how do we deal with that problem. Because I feel that it is a problem. In that there's a ton of context about how an event came to be bound. It is important to know.
26:01
So if we take a look at, let's see. Here's where we're going to get into some very ugly code that I am, I don't know if embarrassed is the right word. But definitely not proud of. So let's see which one is first. So here's some code that I'm going to bring in.
26:24
Now what this is doing is it's, I've heard it referred to as monkey patching, as duck punching. I don't know how well those translate into Norwegian. What we're going to do is we're going to change what the API of our native code is. We're going to tell it to do something different.
26:42
In this particular case, I'm going to rewrite what add event listener does. And I'm going to introduce another property on it. So in addition to passing the name of an event, like click, and the callback to execute. I'm also going to pass through a function that I want to be called when an error happens.
27:01
So that I can choose how to do that. And then I do that with a bunch of JavaScript trickery that isn't all that important. Other than I'm calling back through to the original add event listener. But I'm calling it with my function, my callback, not your callback. And my callback tries to execute your callback.
27:23
And when your callback, how many times can I say callback in a single sentence? My callback calls your callback. When your callback doesn't do what your callback is supposed to do. It calls back into my callback where I pass it to the error callback that you passed through on the original callback. Is anybody counting? So that's what I'm doing here.
27:40
Is I'm trying to execute your function. I'm catching if it errors. And then I'm passing any errors to the global. So let's include that here. That's not what I'm doing. So that file is called listener with error. And what that allows me to do is I just extended the API of add event listener.
28:01
And I pass my on error function right through with everything else. And now if we load our application, I catch the error. I'm able to catch it in my error handler to not pass back out into the native code. But you'll notice that there's some cruft here, right?
28:20
The stack trace still isn't everything. There's some garbage in that stack trace from how I duck punched that function. The error did come from on click as we expected. But then there's this other crap that I had built where I had wrapped up your callback. You really don't want to see that. But then where's all the stuff before that? Where's the stuff before the event happened, right?
28:43
I want to know how did that application come to be bound? Where's initialize in this? Where's the main closure? How do I trace back to the beginning of time for this stack trace? I think that's a very interesting problem. I think there's a lot of opportunity there. So I wrote this other thing, which kind of takes the same idea and just goes a little bit farther.
29:08
That's not it. And so what this is, is it's the same kind of thing. I'm duck punching add event listener. And I'm adding a third parameter. This time I'm doing something.
29:21
The act of binding, I do a few things. I actually throw an error at the time of binding. And then I grab its stack trace. Just because I want a copy of it. I'm going to save it for later. I'm also going to grab the time that you bound the event. And then when I go through to try and execute your function,
29:42
if it breaks, now I have two stack traces. I have the stack trace after the event broke. And I can reference back into my closure and say, give me the stack trace that existed before we bound the event. And if we do that, I'm in the wrong thing.
30:04
I didn't bring it through, sorry. Man, totally ruined it. Async listener. So now when we do it, check out this stack trace. I think this is cool.
30:21
I don't know about you guys, but I think this is amazing. That I can tell so much more about my application and how it died from this. I can see that the application died in on click. I still have a little bit of garbage in there from how I'm doing things with this add event listener callback. But then I know that 1342 milliseconds passed between when the event, when the callback executed versus when it was bound.
30:43
I can tell you how long did the native code sit there thinking about or waiting for this event to happen. And that event came to be bound with some more of my garbage from here I did it. But initialize bound the event, which was called from main, which was executed from our main entry closure.
31:00
And so now I have a full chain of events that I can look at and reason about my code base here. I can see exactly what happened the entire way. I can see that, hey, on click died, garbage, garbage, garbage, called by initialize. And I can see both sides of this. And this is kind of trivial in this little application, but has anybody built, like, large scale
31:21
JavaScript applications where they might have hundreds or thousands of JavaScript files split over a code base? It can be very important to see some of this. But there's more callback functions than just, you know, add event listener. There's other things that we do. So maybe my application wants to do something like, you know, four seconds after load, I want to do something.
31:44
I want to, I should probably implement an afterload function, huh? Function afterload. We're going to, oh, I don't know, throw a new error. You know, you probably don't actually want to do that four seconds afterload. But, you know, this is an air talk.
32:03
A timed error. So four seconds afterload, I blow up for a different reason. But I want to understand not just the add event listener, I want to do all of them. So if we look at, you know, I took the same idea and I just went, you know, one step farther.
32:21
And so rather than just duck punching add event listener, I kind of figured out a way to duck punch any function. And so in this case, I'm overriding set timeout as well. And add event listener. And how did I do that?
32:47
Oh, yep, sorry, sorry, my bad. What I did here is because I wanted to wrap this at such a low level, I just wanted to insert this into any project I wanted to work on. And I didn't want to have to change the API of, like, add event listener.
33:04
I didn't want to, because oftentimes we'll use jQuery or use Knockout or use Backbone or Angular or something. And you're operating at a much higher level of abstraction than actually manipulating base host functions. And so you can't change that API without, like, throwing out a ton of work. But wouldn't it be great if, like, we could leverage some of the infrastructure that's already there?
33:24
What if we could just overuse window dot on error from any function I wanted? So in this particular version, this also duck types or duck punches the base level host functions. But rather than taking an on error callback, we just call directly into a function on window on error.
33:46
But I don't let the native code do it. I do it. And I have full context. So I'm going to pass the name. I'm going to pass the line number. I'm going to pass the column number. I'm going to file name. And I'm going to pass the error object itself. And in this way, I can totally beat out, oh, man, I have to implement it again.
34:04
That was async error. And now I don't have to even pass that in. And now if I run this guy, I've implemented proper window on error for Firefox.
34:24
Because I've not hit the native code at all. I've built out my own asynchronous stack trace the way I want to build it. And I've passed it to the global error handler, which I'm already expecting to exist and is already guaranteed to be there in all browsers. But I'm not letting the native code with its limited implementation worry about it.
34:41
Also because I've called it and I haven't relied on the browser to call it. I have to actually go to it. You'll see that it is, it has the same error object in all browsers. I don't know why it doesn't have those other things.
35:05
Now I actually have the same implementation, the same details in all of the browsers because I've taken control of it. Now I know that that is some terribly ugly code. And I fully recognize that. But I think this is interesting.
35:21
And I think this is valuable. And if we can find a way to get here that doesn't have some of the drawbacks, I think this could totally change how we go about debugging and understanding our web applications. And when we can debug and understand our web applications, that makes me as the developer and the support person really happy because it makes my customer happy.
35:47
Because that awesome thing that we built together, like it didn't get ruined by something terrible happening. And so that's what I had to show about async paths. But I want to leave you guys with a counterpoint.
36:00
Is that this talk was all about how to get stack traces and how to get better stack traces. But I've developed a lot of JavaScript. And stack traces aren't everything. A lot of times the stack trace doesn't give you anything of value at all. Here's a real one. I scraped this out of a project I was working on. I have no idea what's happening here.
36:22
Somewhere in here I screwed something up. I know it's me screwing up. I'm sure jQuery is fine. Somewhere I did something terrible. But I have no idea what it was. Here's another example from Angular. Again, I'm sure it's my fault. But I have no idea what I did wrong. And so the stack trace by itself isn't really enough for us to reason about our applications.
36:45
And so I've been thinking a lot about how can you capture more context. And this is like specific to your application. So I'm kind of asking you to think about your applications. How do you know what your user was doing? What information can you record about them to know like what did they click on?
37:01
What did they go? What page did they go to? What state was my application in and how did it get there? And what information would it make sense for me to record about the state of my application so that when it blows up, you know something about it? And then what was happening in the overall environment? Maybe the user has like a really crappy Chrome extension installed on their browser
37:21
and it's like interfering with your application. Or maybe it's just you're getting weird Ajax timing and your calls aren't coming back in the order that you would have expected them to. And so I'm working on a little project called TrackJS where we're trying to solve some of this. And we're trying to build a way to track what the user network and consoles are doing as part of the application
37:42
so that when an error happens, we can try and give you some more context. This is me. Here's my contact information. I'm going to hang out and talk about JavaScript and hacking and some very, very ugly code. If you'd like to chat with me now or later, that's cool. Here's how to get a hold of me. Thank you very much for your time.