6

I'm using angular-cli to build a small angular2 web app and I'm debugging with chrome dev-tools.

Clearly I'm doing something wrong if I need to guess each time where is the source of the error and what is the stack-trace of that error.

Take for example this error:

error_handler.js:45EXCEPTION: Cannot read property 'provider' of nullErrorHandler.handleError @ error_handler.js:45
error_handler.js:50ORIGINAL STACKTRACE:ErrorHandler.handleError @ error_handler.js:50
error_handler.js:51TypeError: Cannot read property 'provider' of null
    at MapSubscriber.project (auth.effects.ts:80)
    at MapSubscriber._next (map.js:77)
    at MapSubscriber.Subscriber.next (Subscriber.js:89)
    at DistinctUntilChangedSubscriber._next (distinctUntilChanged.js:72)
    at DistinctUntilChangedSubscriber.Subscriber.next (Subscriber.js:89)
    at MapSubscriber._next (map.js:83)
    at MapSubscriber.Subscriber.next (Subscriber.js:89)
    at MapSubscriber._next (map.js:83)
    at MapSubscriber.Subscriber.next (Subscriber.js:89)
    at RefCountSubscriber.Subscriber._next (Subscriber.js:125)ErrorHandler.handleError @ error_handler.js:51
zone.js:355Unhandled Promise rejection: Cannot read property 'provider' of null ; Zone: <root> ; Task: Promise.then ; Value: TypeError: Cannot read property 'provider' of null(…) TypeError: Cannot read property 'provider' of null
    at MapSubscriber.project (http://localhost:4200/main.bundle.js:35342:83)
    at MapSubscriber._next (http://localhost:4200/main.bundle.js:4171:35)
    at MapSubscriber.Subscriber.next (http://localhost:4200/main.bundle.js:395:18)
    at DistinctUntilChangedSubscriber._next (http://localhost:4200/main.bundle.js:25485:30)
    at DistinctUntilChangedSubscriber.Subscriber.next (http://localhost:4200/main.bundle.js:395:18)
    at MapSubscriber._next (http://localhost:4200/main.bundle.js:4177:26)
    at MapSubscriber.Subscriber.next (http://localhost:4200/main.bundle.js:395:18)
    at MapSubscriber._next (http://localhost:4200/main.bundle.js:4177:26)
    at MapSubscriber.Subscriber.next (http://localhost:4200/main.bundle.js:395:18)
    at RefCountSubscriber.Subscriber._next (http://localhost:4200/main.bundle.js:431:26)consoleError @ zone.js:355
zone.js:357Error: Uncaught (in promise): TypeError: Cannot read property 'provider' of null
    at resolvePromise (http://localhost:4200/main.bundle.js:93214:31)
    at http://localhost:4200/main.bundle.js:93191:13
    at ZoneDelegate.invoke (http://localhost:4200/main.bundle.js:92988:28)
    at Zone.run (http://localhost:4200/main.bundle.js:92881:43)
    at http://localhost:4200/main.bundle.js:93247:57
    at ZoneDelegate.invokeTask (http://localhost:4200/main.bundle.js:93021:37)
    at Zone.runTask (http://localhost:4200/main.bundle.js:92921:47)
    at drainMicroTaskQueue (http://localhost:4200/main.bundle.js:93153:35)consoleError @ zone.js:357 

The problem:

This errors means nothing to me. It's completely uselss and un readble. I got lucky that I saw this line (sometimes I dont get any indication where the error is): at MapSubscriber.project (auth.effects.ts:80) - This line is the only line that usefull here to get some idea how to fix that bug. Trying to understand the stack-trace will be pointless because its all rxjs stack-trace.

My question:

I would like to know my code's stack-trace. Is that possible?

  1. where in my code the subscription to that observable happens.
  2. If its an observable from ngrx, then where in my code someone dispanched that action that causes the error.

Its more general question about how to debug async code with rxjs then fixing this specific bug.

Stav Alfi
  • 13,139
  • 23
  • 99
  • 171

1 Answers1

1

It happened in the projection method you provided to a map operator. The clue is at the top of your stack. MapSubscriber.project.

Basically you read stack traces from the top. The top most call is where the error was thrown (or rethrown).

In RxJS 5, there are usually two or three calls per operator. Each operator has a subscriber named after it that does the work. MapSubscriber.Subscriber.next MapSubscriber._next etc

Ben Lesh
  • 107,825
  • 47
  • 247
  • 232
  • 1
    thanks for the information. Basicly you saying that there is no way to know where in my code someone subscribed to that observable that causes the error? – Stav Alfi Oct 16 '16 at 14:17
  • @StavAlfi it's impossible to have a stack track of code that isn't on the stack when the error occurs. When you call `subscribe`, you're listening for events to be emitted. If it doesn't immediately error _when_ you call subscribe, that invocation is not on the call stack. If an error occurs inside your observable chain (in this case, your project function you provide to the map operator) it never reaches your observer; the callback you provided to subscribe so again it's not on the stack either. – jayphelps Oct 19 '16 at 04:19
  • Okay thanks @JayPhelps. I hope you guys will build an extra stack containing only the async opertions so it will be easier to debug and get more control. – Stav Alfi Oct 19 '16 at 05:26
  • Can you clarify what would be ideal? We've worked extra hard to make the various possible stacks significantly more clear than they were in v4, and it's not clear how we could improve them significantly. Perhaps there's misunderstanding? – jayphelps Oct 19 '16 at 06:12
  • When your Observable is emitting items, that stack is unrelated to the stack of where you defined the stream via operators like `observable.debounceTime(100).map(i => i + 1)` etc. When you invoke operators, it's basically setting up "pipes" and the operators are like instructions, telling Rx to do this for each item emitted, whenever they arrive. This differs from array methods `array.map(i => i + 1)` because with an array you're working on the values _right now_ because they exist, synchronously. Whereas the items your Observable emits [usually] come in the future, async. – jayphelps Oct 19 '16 at 06:14
  • @JayPhelps Thank you for your consideration. Let me rephrase myself. I would like to suggest that you guys will build a new component that demonestrate the stack of the functions that the rxjs excute when an error comes up. for example the code: `Observable.from([1,2,3]).map(num=>num+1).do(_=>throw "my error"); => this will be shown in the "component stack" as "rxFile line X -> rxFile line X+5 -> .... error in rxFile line X +80. By that I can easly find what is broken and why (there will be no breakpoints but at list we get some control). I hope I made my self clear. Thanks alot! – Stav Alfi Oct 19 '16 at 18:45