Run ngrx/effect outside of Angular's zone to prevent timeout in Protractor

后端 未结 2 459
盖世英雄少女心
盖世英雄少女心 2021-02-01 09:53

I just started to write e2e tests for my app and am running into timeout problems with Protractor and ngrx/effects.

I have the following effect dispatching an action ev

相关标签:
2条回答
  • 2021-02-01 10:28

    For Angular 6 and RxJS 6 use the following code:

    import { SchedulerLike, Subscription } from 'rxjs'
    import { NgZone } from '@angular/core'
    
    class LeaveZoneScheduler implements SchedulerLike {
      constructor(private zone: NgZone, private scheduler: SchedulerLike) { }
    
      schedule(...args: any[]): Subscription {
        return this.zone.runOutsideAngular(() =>
          this.scheduler.schedule.apply(this.scheduler, args)
        )
      }
    
      now (): number {
        return this.scheduler.now()
      }
    }
    
    class EnterZoneScheduler implements SchedulerLike {
      constructor(private zone: NgZone, private scheduler: SchedulerLike) { }
    
      schedule(...args: any[]): Subscription {
        return this.zone.run(() =>
          this.scheduler.schedule.apply(this.scheduler, args)
        )
      }
    
      now (): number {
        return this.scheduler.now()
      }
    }
    
    export function leaveZone(zone: NgZone, scheduler: SchedulerLike): SchedulerLike {
      return new LeaveZoneScheduler(zone, scheduler)
    }
    
    export function enterZone(zone: NgZone, scheduler: SchedulerLike): SchedulerLike {
      return new EnterZoneScheduler(zone, scheduler)
    }
    

    The effect should look like:

    import { asyncScheduler } from 'rxjs'
    import { filter, observeOn, bufferTime } from 'rxjs/operators'
    import { enterZone, leaveZone } from './util';
    
    actions$.ofType('[Light] Turn On')
      .pipe(
        bufferTime(300, leaveZone(this.ngZone, asyncScheduler)),
        filter(messages => messages.length > 0),
        observeOn(enterZone(this.ngZone, asyncScheduler)),
      )
    
    0 讨论(0)
  • 2021-02-01 10:34

    The solution is to schedule the timer observable to run outside of NgZone and then re-enter the zone when something interesting occurs.

    First you are going to need two utility functions that wrap any scheduler and cause the effect to enter or leave the zone:

    import { Subscription } from 'rxjs/Subscription';
    import { Scheduler } from 'rxjs/Scheduler';
    import { NgZone } from '@angular/core';
    
    
    class LeaveZoneSchduler {
      constructor(private zone: NgZone, private scheduler: Scheduler) { }
    
      schedule(...args: any[]): Subscription {
        return this.zone.runOutsideAngular(() => 
            this.scheduler.schedule.apply(this.scheduler, args)
        );
      }
    }
    
    class EnterZoneScheduler {
      constructor(private zone: NgZone, private scheduler: Scheduler) { }
    
      schedule(...args: any[]): Subscription {
        return this.zone.run(() => 
            this.scheduler.schedule.apply(this.scheduler, args)
        );
      }
    }
    
    export function leaveZone(zone: NgZone, scheduler: Scheduler): Scheduler {
      return new LeaveZoneSchduler(zone, scheduler) as any;
    }
    
    export function enterZone(zone: NgZone, scheduler: Scheduler): Scheduler {
      return new EnterZoneScheduler(zone, scheduler) as any;
    }
    

    Then using a scheduler (like asap or async) you can cause a stream to enter or leave the zone:

    import { async } from 'rxjs/scheduler/async';
    import { enterZone, leaveZone } from './util';
    
    actions$.ofType('[Light] Turn On')
        .bufferTime(300, leaveZone(this.ngZone, async))
        .filter(messages => messages.length > 0)
        .observeOn(enterZone(this.ngZone, async))
    

    Note that most of the time-based operators (like bufferTime, debounceTime, Observable.timer, etc) already accept an alternative scheduler. You only need observeOn to re-enter the zone when something interesting happens.

    0 讨论(0)
提交回复
热议问题