17

Merry Christmas to you mate.
My Angular 4 app will not await.

I want to slow down before API I call.
but I just continue to hit a wall.

I'm using a HttpInterceptor in my code.
but these Observable will just explode.

To not get too much contempt.
below you'll find my attempt.

export class ApiUrlInterceptor implements HttpInterceptor {

    constructor(private http: Http) { }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return Observable.create(observer => {
            setTimeout(() => {
                observer.next(true); //Not sure why I do this
                const start = Date.now();
                console.log(`Request for ${req.url}`);
                return next.handle(req).do(event => {
                    if (event.type == HttpEventType.Response) {
                        const elapsed = Date.now() - start;
                        console.log(`Request for ${req.urlWithParams} took ${elapsed} ms.`);
                    }
                });
            }, 1000);
        });
    }
}

The result is that API is called.
but no result to caller is installed

My Observable seems to be stuck.
and I'm running out of luck.


I'm well aware that this is an anti-pattern in Angular "don't wait for a random number", instead structure your application so you do not need to. My actual use case was that in HttpInterceptor I need some stuff that is loaded by other Observable, normally I don't have troubles, only if users refresh a particular page I had a risk that the stuff was not loaded.

My direct taught "quick fix" was heck I make an if and if not loaded I wait for some ("give it time to load") then I proceed, who cares! the user will not refresh that particular angular link often. I ended up the long way move all to a config.ts and use APP_INITIALIZER. However, still, I want to know how to wait for some time if I like to wait, hence this minimal example.

Saheb
  • 1,190
  • 2
  • 12
  • 28
Petter Friberg
  • 19,652
  • 9
  • 51
  • 94
  • 5
    I was going to edit your question, but then I noticed it was a sort of song – Serge K. Dec 21 '17 at 15:45
  • Yeah just some fun, I'm bored, anyway I hope it's clear if I'm missing some info let me know – Petter Friberg Dec 21 '17 at 15:46
  • Why do you want to "slow it down"? What are you trying to achieve (what is your end goal)? – Igor Dec 21 '17 at 15:47
  • 6
    nice festive question format – mxr7350 Dec 21 '17 at 15:48
  • @Igor it's an example minimized code, the end objective is to slow down, await (time that I like to) before I call api. – Petter Friberg Dec 21 '17 at 15:50
  • But *why* do you want to wait until you call the api? Are you collecting data or do you want to replace the call with another one if additional input is received or something else? – Igor Dec 21 '17 at 15:51
  • So basically you want to add a (fixed) pre-request delay to all of your outgoing requests? – Jota.Toledo Dec 21 '17 at 15:53
  • @Igor To be honest it was a config issue, wait before I call since I need to load some other stuff, I worked around that issue using `APP_INITIALIZER`, but still I like to wrap my head around these Observable and understand what is not working, why and how I should consume it before I return. – Petter Friberg Dec 21 '17 at 15:54
  • @Jota.Toledo exactly, I want to delay the call. – Petter Friberg Dec 21 '17 at 15:54
  • It sounds like you could achieve a better solution by structuring your calls so they are executed in a specific order. This can also be done using the RxJs library. Alternatively you can sequence your calls by nesting the call in the `subscribe` method of the previously completed call. Either way I do not think hard coding a delay is the best way to go about it because if the system is non-responsive for longer than you have hard coded your code will likely error out (somewhere). – Igor Dec 21 '17 at 15:57
  • If this sounds like your problem (*I am guessing as I can't see the rest of your code*) then please add the code that has prompted you to write this solution and clearly which calls should be sequenced in a specific order. – Igor Dec 21 '17 at 15:59
  • I did find this existing [question/answer](https://stackoverflow.com/questions/37748241/how-to-do-the-chain-sequence-in-rxjs) that might better answer your actual problem. Using flatmap you can chain observables so they run in sequence and then subscribe to the resulting observable from the chain. – Igor Dec 21 '17 at 16:01
  • @Igor this simplified example just like to delay the api, above it's hardcoded 1s but naturally that could be a variabile. In fact I solved my original problem in other way, but my curiosity remains how can I delay call to api if I like. Thanks for link, I test some flatmap but with no success but I will study it again to see if I manage to wrap my head around it. – Petter Friberg Dec 21 '17 at 16:02
  • can you elaborate a bit on the use case for delaying the subscription? I want to create a feature request to add delay to the `defer` operator and I need some justification – Max Koretskyi Dec 21 '17 at 18:10
  • @AngularInDepth.com I have edited the post to add the reason of post, I actually solved my direct problem in an other way and can understand the perplexity of user as Igor "you should not wait for a random number", restructure your application. My only justification is "if I like to wait let me wait!", if this is quicker then restructuring application let me do it. Sometimes the perfect solution is not the best one, since unfortunately still time is $. I could come up with some strange use-case load balance of api (throttling) but that would not be the truth. Just let me wait. HECK no hurry. – Petter Friberg Dec 21 '17 at 20:04

1 Answers1

7

Do not despair
Instead here have a glare

import { timer } from 'rxjs/observable/timer';
// or import { TimerObsevable } from 'rxjs/observable/TimerObsevable';

export class PreRequestDelayInterceptor implements HttpInterceptor {

    constructor(@Inject(PRE_REQUEST_DELAY)@Optional() private delay = 1000) { }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
       const delay = timer(this.delay);
       const start = Date.now();
       return delay.switchMap(()=> next.handle(req))
       .do(event => {
            if (event.type == HttpEventType.Response) {
               const elapsed = Date.now() - start;
               console.log(`Request for ${req.urlWithParams} took ${elapsed} ms.`);
            }
        });
    }
}

By using an InjectionToken you can inject a fixed delay. If none is provided, 1000 will be the default delay.

Petter Friberg
  • 19,652
  • 9
  • 51
  • 94
Jota.Toledo
  • 22,272
  • 8
  • 48
  • 63
  • 1
    This solution works for me, nice touch injecting the delay with default value, even if my case it would have arrived from another class (injected), I hope you don't mind my minor edit just to keep the Christmas spirit :) – Petter Friberg Dec 22 '17 at 11:40
  • ahhah ofc not ;) – Jota.Toledo Dec 22 '17 at 12:25