Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

229
Visualizações
Run ngrx/effect outside of Angular's zone to prevent timeout in Protractor

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 every couple of minutes:

@Effect() setSessionTimer$ = this.actions$
        .ofType(Auth.ActionTypes.SET_SECONDS_LEFT)
        .map(toPayload)
        .switchMap(secondsLeft => Observable.concat(
            Observable.timer((secondsLeft - 60) * 1000).map(_ => new Auth.SessionExpiringAction(60)),
            Observable.timer(60 * 1000).map(_ => new Auth.SessionExpiredAction())
        ));

Trying to run a Protractor test causes the test to timeout with the following error, since Angular is not stable.

Failed: Timed out waiting for asynchronous Angular tasks to finish after 11 seconds. This may be because the current page is not an Angular application. Please see the FAQ for more details: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular While waiting for element with locator - Locator: By(css selector, .toolbar-title)

According to this issue (https://github.com/angular/protractor/issues/3349) I need to use NgZone to run an interval Observable outside of Angular. I have tried different combinations of this.ngZone.runOutsideAngular() but nothing worked and the tests keep on timing out.

For example this does not work:

@Effect() setSessionTimer$ = this.actions$
        .ofType(Auth.ActionTypes.SET_SECONDS_LEFT)
        .map(toPayload)
        .switchMap(secondsLeft => this.ngZone.runOutsideAngular(() => Observable.concat(
            Observable.timer((secondsLeft - 60) * 1000).map(_ => new Auth.SessionExpiringAction(60)),
            Observable.timer(60 * 1000).map(_ => new Auth.SessionExpiredAction())
        )));

I have no idea how to run the effect outside Angular. Has anybody successfully e2e tested their ngrx app?

about 4 years ago · Santiago Trujillo
2 Respostas
Responde à pergunta

0

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.

about 4 years ago · Santiago Trujillo Relatório

0

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, queueScheduler } 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, queueScheduler)),
  )
about 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda