title: Angular : how to debounce an Observable?
body: <p>In my app, I have a service that returns an observable like this :</p>
  
  <pre><code>public genericService(params) {
      //Do some stuff
      //...
  
      return this.http.post('http://foo.com', params)
          .map((response) =&gt; {
              //Do some generic stuff
              //...
  
              return someData;
          })
          .catch((error: any) =&gt; {
              //Manage error in a generic way + do some generic stuff
              //...
  
              return Observable.throw(error);
          });
  }
  
  let debouncePointer = debounceObservable(genericService, 200);
  
  public genericServiceDebounce(params) {
      return debouncePointer(params);
  }
  </code></pre>
  
  <p>Now in another place, I would like to call my function like this</p>
  
  <pre><code>genericServiceDebounce(params)
      .subscribe((response) =&gt; {
          //Do some non-generic stuff
      }, (error) =&gt; {
          //Manage error in a non-generic way + do some non-generic stuff
      });
  </code></pre>
  
  <p><strong>But I didn't succeed to implement the debounceObservable() function.</strong></p>
  
  <p>I tried this implementation based on a Promise equivalent ( <a href="https://github.com/moszeed/es6-promise-debounce/blob/master/src/es6-promise-debounce.js" rel="nofollow noreferrer">https://github.com/moszeed/es6-promise-debounce/blob/master/src/es6-promise-debounce.js</a> ) :</p>
  
  <pre><code>debounceObservable(callback, delay, immediate?) {
      let timeout;
      return function () {
          let context = this, args = arguments;
  
          return Observable.create((observer) =&gt; {
              let later = function () {
                  timeout = null;
  
                  if(!immediate) {
                      observer.next(callback.apply(context, args));
                      //observer.onCompleted(); // don't know if this is needed
                  }
              };
              let callNow = immediate &amp;&amp; !timeout;
              clearTimeout(timeout);
              timeout = setTimeout(later, delay);
  
              if(callNow) {
                  observer.next(callback.apply(context, args));
                  //observer.onCompleted(); // don't know if this is needed
              }
          });
      }
  }
  </code></pre>
  
  <p>But this don't work as expected. 
  When using Promises, returning resolve(anotherPromise) allows you to call :</p>
  
  <pre><code>genericServiceDebounce().then(response =&gt; {
  
  })
  </code></pre>
  
  <p>When using Observables, returning observer.next(anotherObservable) return an embedded observable, which means you should call :</p>
  
  <pre><code>genericServiceDebounce().subscribe(obs =&gt; {
      obs.subscribe(response =&gt; {
  
      })
  })
  </code></pre>
  
  <p><strong>How would you implement the debounceObservable() function?</strong> (in a Promise like way)</p>
  
  <p><em>Clarification 1 : I found the Observable.debounce() function but this debounces the observer and not the observable itself. And I want to debounce the observable</em></p>
  
  <p><em>Clarification 2 : I placed the debounce on the service side because it is a singleton, and their are multiple callers. If I placed it on caller side, there would be a different debounce timer for each caller.</em></p>
  
  <p>EDIT : Here is a snippet where I try to explain my problem. Just click the different buttons to see the different behaviors (more explanation in js code comments).</p>
  
  <p><strong>Observable.debounce</strong> shows how .debounce() from RxJs works. It outputs only '3' but I want '1', '2', '3'.</p>
  
  <p><strong>Observable.debounce x3</strong> shows what happens if I call the code 3 times without wrapping my entire function in a debounce.</p>
  
  <p><strong>Observable wrapped x3</strong> shows what I want to obtain. My entire function is wrapped, but if you look at the code, the subscribe part is fastidious.</p>
  
  <p><strong>Promise x3</strong> shows how simple it is when using Promises.</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="true">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>let log = (logValue) =&gt; {
      const list = document.querySelector('#logs');
      const li = document.createElement('li');
      li.innerHTML = logValue;
      list.appendChild(li);
  }
  
  /* ************************ */
  /* WITH OBSERVABLE.DEBOUNCE */
  /* ************************ */
  
  let doStuffObservable = () =&gt; {
      Rx.Observable.create((observer) =&gt; {
          log('this should be called only one time (observable.debounce)');
          setTimeout(() =&gt; {
              observer.next('observable.debounce 1');
              observer.next('observable.debounce 2');
              observer.next('observable.debounce 3');
          }, 1000);
      })
          .debounce(500)
          .subscribe((response) =&gt; {
              log(response);
          }, (error) =&gt; {
              log(error);
          });
  }
  
  /* *********************************** */
  /* WITH OBSERVABLE WRAPPED IN DEBOUNCE */
  /* *********************************** */
  
  let doStuffObservable2 = (param) =&gt; {
      return Rx.Observable.create((observer) =&gt; {
          log('this should be called only one time (observable wrapped)');
          setTimeout(() =&gt; {
              observer.next('observable wrapped ' + param);
          }, 1000);
      })
  }
  
  let debounceObservable = (callback, delay, immediate) =&gt; {
      let timeout;
      return function () {
          let context = this, args = arguments;
  
          return Rx.Observable.create((observer) =&gt; {
              let later = function () {
                  timeout = null;
  
                  if(!immediate) {
                      observer.next(callback.apply(context, args));
                  }
              };
              let callNow = immediate &amp;&amp; !timeout;
              clearTimeout(timeout);
              timeout = setTimeout(later, delay);
  
              if(callNow) {
                  observer.next(callback.apply(context, args));
              }
          });
      }
  }
  
  let doStuffObservable2Debounced = debounceObservable(doStuffObservable2);
  
    
  /* ************* */
  /* WITH PROMISES */
  /* ************* */
  
  let doStuffPromise = (param) =&gt; {
      return new Promise((resolve, reject) =&gt; {
          log('this should be called only one time (promise)');
          setTimeout(() =&gt; {
              resolve('promise ' + param);
          }, 1000);
      });
  }
  
  let debouncePromise = (callback, delay, immediate) =&gt; {
      let timeout;
      return function () {
          let context = this, args = arguments;
          return new Promise(function (resolve) {
              let later = function () {
                  timeout = null;
                  
                  if (!immediate) {
                      resolve(callback.apply(context, args));
                  }
              };
              let callNow = immediate &amp;&amp; !timeout;
              clearTimeout(timeout);
              timeout = setTimeout(later, delay);
              
              if (callNow) {
                  resolve(callback.apply(context, args));
              }
          });
      }
  }
  
    
  /* ******* */
  /* SAMPLES */
  /* ******* */
  
  function doObservableDebounce() {
    doStuffObservable();
    
    // result :
    
        // this should be called only one time (observable.debounce)
        // observable.debounce 3
    
    // this is not what i want, i want all three values in output
  }
  
  function doObservableDebounce3Times() {
    doStuffObservable();
    doStuffObservable();
    doStuffObservable();
    
    // result :
    
        // this should be called only one time (observable.debounce)
        // this should be called only one time (observable.debounce)
        // this should be called only one time (observable.debounce)
        // observable.debounce 3
        // observable.debounce 3
        // observable.debounce 3
    
    // this is bad
  }
  
  function doObservableWrappedDebounce3Times() {
    doStuffObservable2Debounced(1)
        .subscribe((response) =&gt; {
            log(response);
            response.subscribe((response2) =&gt; {
                log(response2);
            }, (error) =&gt; {
                log(error);
            })
        }, (error) =&gt; {
            log(error);
        });
    doStuffObservable2Debounced(2)
        .subscribe((response) =&gt; {
            log(response);
            response.subscribe((response2) =&gt; {
                log(response2);
            }, (error) =&gt; {
                log(error);
            })
        }, (error) =&gt; {
            log(error);
        });
    doStuffObservable2Debounced(3)
        .subscribe((response) =&gt; {
            log(response);
            response.subscribe((response2) =&gt; {
                log(response2);
            }, (error) =&gt; {
                log(error);
            })
        }, (error) =&gt; {
            log(error);
        });
    
    
    // result :
    
        // AnonymousObservable { source: undefined, __subscribe: [Function] }
        // this should be called only one time (observable wrapped)
        // observable wrapped 3
    
    // this is good but there are 2 embedded subscribe
  }
  
  function doPromiseDebounce3Times() {
    let doStuffPromiseDebounced = debouncePromise(doStuffPromise);
    
    doStuffPromiseDebounced(1).then(response =&gt; {
        log(response);
    })
    doStuffPromiseDebounced(2).then(response =&gt; {
        log(response);
    })
    doStuffPromiseDebounced(3).then(response =&gt; {
        log(response);
    })
    
    // result :
    
        // this should be called only one time (promise)
        // promise 3
    
    // this is perfect
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;!DOCTYPE html&gt;
  &lt;html&gt;
  
    &lt;head&gt;
      &lt;script data-require="rxjs@4.0.6" data-semver="4.0.6" src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.6/rx.all.js"&gt;&lt;/script&gt;
    &lt;/head&gt;
  
    &lt;body&gt;
      &lt;button onclick='doObservableDebounce()'&gt;Observable.debounce&lt;/button&gt;
      &lt;button onclick='doObservableDebounce3Times()'&gt;Observable.debounce x3&lt;/button&gt;
      &lt;button onclick='doObservableWrappedDebounce3Times()'&gt;Observable wrapped x3&lt;/button&gt;
      &lt;button onclick='doPromiseDebounce3Times()'&gt;Promise x3&lt;/button&gt;
      &lt;ul id="logs"&gt;&lt;/ul&gt;
    &lt;/body&gt;
  
  &lt;/html&gt;</code></pre>
  </div>
  </div>
  </p>