Search code examples
javascriptrxjssettimeout

Oberver.next() is not a function


I want to print hello world

 const obs = new Observable( (observer)=>{ setTimeout(observer.next, 0) } );
 obs.subscribe(()=>{console.log("world")});
 console.log("hello ");

This gives an error this._next is not a function

If I instead write the following it works fine:

 const obs = new Observable( (observer)=>{ setTimeout(()=>{observer.next()}, 0) } );
 obs.subscribe(()=>{console.log("world")});
 console.log("hello ");

Why can't I pass the reference to observer.next callback directly to setTimeout()?


Solution

  • In RXJS the internals of the .next() function use this. To get a better understanding, the next method is defined like so:

    next(value: T): void {
      if (this.isStopped) {
        handleStoppedNotification(nextNotification(value), this);
      } else {
        this._next(value!);
      }
    }
    

    The value of this inside of this method depends on how it is called (see this for more info). When you call it using:

    observer.next()
    

    as you do in your second example, the this value gets set to the observer object you called the next method on, allowing it to function correctly when the internals eventually end up calling ._next() on the observer object. However, when you pass the next function reference to setTimeout() in your first example:

    setTimeout(observer.next, 0) 
    

    you lose the this context (that being the observer), as now you're just passing the function reference of next to setTimeout(), and not calling the method on a particular object anymore. When setTimeout() eventually invokes the next function after the specified delay, it doesn't have any information about where the function it's calling came from, and so the this value inside of the next method once its invoked defaults to the global object, or undefined in strict mode, which both don't have _next() methods.

    An alternative would be to .bind() the observable to the function, which returns a new function with the this value set explicitly to the observable:

    setTimeout(observer.next.bind(observer), 0)