zoneangular18

Timeout since the migration to angular 18


Since the upgrade to angular18, I'm having timeout with simple component

[vite] Internal server error: Page /guide did not render in 30 seconds.
      at Timeout.<anonymous> (C:\Users\mbagi\Developer\xxx\angular-client\node_modules\@angular\build\src\utils\server-rendering\render-page.js:90:90)
      at Timeout.timer (c:/Users/mbagi/Developer/xxx/angular-client/node_modules/zone.js/fesm2015/zone-node.js:2320:21)
      at _ZoneDelegate.invokeTask (c:/Users/mbagi/Developer/xxx/angular-client/node_modules/zone.js/fesm2015/zone-node.js:459:13)
      at _ZoneImpl.runTask (c:/Users/mbagi/Developer/xxx/angular-client/node_modules/zone.js/fesm2015/zone-node.js:226:35)
      at invokeTask (c:/Users/mbagi/Developer/xxx/angular-client/node_modules/zone.js/fesm2015/zone-node.js:540:14)
      at Timeout.ZoneTask.invoke (c:/Users/mbagi/Developer/xxx/angular-client/node_modules/zone.js/fesm2015/zone-node.js:524:33)
      at Timeout.data.args.<computed> (c:/Users/mbagi/Developer/xxx/angular-client/node_modules/zone.js/fesm2015/zone-node.js:2301:23)
      at listOnTimeout (node:internal/timers:573:17)
      at process.processTimers (node: internal/timers:514:7

The component is used to render an image once every second, it takes into parameters the list of images

import { Component, OnInit, signal } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    @if(img(); as img){
      <img [src]="img" />
    }
  `,
  styles: '',
})
export class AppComponent implements OnInit {
  imgs = ['https://cdn.pixabay.com/photo/2023/09/02/03/15/water-8228076_1280.jpg',
  'https://media.istockphoto.com/id/157482223/de/foto/water-splash.jpg?s=2048x2048&w=is&k=20&c=tovlRmZEzpSmlXEL9OH8iANIK2w16YQD8QDDtsmxs3U=',
  'https://media.istockphoto.com/id/157482222/de/foto/gefrorene-tropfen-wasser.jpg?s=2048x2048&w=is&k=20&c=ASd2SEWIz7EEiSQLeCdrf6zA-eR9ExAyFCzZLG1tXco='];

  img = signal('');
  intervalId:any;
  pointer=0;

  ngOnInit(): void {
    this.img.set(this.imgs[0]);
    this.startImageRotation();
  }

  startImageRotation(): void {
    this.intervalId = setInterval(() => {
      this.pointer = (this.pointer + 1) % this.imgs.length;
      this.img.set(this.imgs[this.pointer]);
    }, 1000);
  }
}

The code is working if I load another page and I navigate to this page, if I try to refresh the page directly there is a TimeOut. However it always fails if I try to run ng build. This page doesn't let me build to the project

How to reproduce the issue in few steps with ANGULAR 18 :

ng new test

Update the app.compenent.ts

import { Component, OnInit, signal } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    @if(img(); as img){
      <img [src]="img" />
    }
  `,
  styles: '',
})
export class AppComponent implements OnInit {
  imgs = `['https://cdn.pixabay.com/photo/2023/09/02/03/15/water-8228076_1280.jpg',
  'https://media.istockphoto.com/id/157482223/de/foto/water-splash.jpg?s=2048x2048&w=is&k=20&c=tovlRmZEzpSmlXEL9OH8iANIK2w16YQD8QDDtsmxs3U=',
  'https://media.istockphoto.com/id/157482222/de/foto/gefrorene-tropfen-wasser.jpg?s=2048x2048&w=is&k=20&c=ASd2SEWIz7EEiSQLeCdrf6zA-eR9ExAyFCzZLG1tXco=']`;

  img = signal('');
  intervalId:any;
  pointer=0;

  ngOnInit(): void {
    this.img.set(this.imgs[0]);
    this.startImageRotation();
  }

  startImageRotation(): void {
    this.intervalId = setInterval(() => {
      this.pointer = (this.pointer + 1) % this.imgs.length;
      this.img.set(this.imgs[this.pointer]);
    }, 1000);
  }
}

then Serve

ng serve

You should have an error like that enter image description here


Solution

  • Looks like you're either using ssr or prerendering. Your app remains unstable because of the pending interval.

    You could start the interval on the client only with afterNextRender()

    afterNextRender(() => {
        this.intervalId = setInterval(() => {
          this.pointer = (this.pointer + 1) % this.imgs.length;
          this.img.set(this.imgs[this.pointer]);
        }, 1000);
    );