'Angular error "expected to not be in angular zone but it is"

I am developing a web app in Angular, and I keep getting this error popping up in the Chrome console: enter image description here

Up until now I didn't bother doing anything about it because it didn't seem to affect the behaviour of the application, but recently I added a new feature which causes this error to pop up thousands upon thousands of times while the app is open, and this is causing Chrome's memory usage to skyrocket.

The research I did on this suggested that I do the following:

  • Add <script src="https://cdnjs.cloudflare.com/ajax/libs/zone.js/0.8.29/zone.js"></script> to the index.html file.
  • Remove import 'Zone.js' from Polyfills.ts.

Neither of which have worked.

The main application page has a number of async functions which all iterate in a loop in order to get data from the backend which is required to keep the page up to date without having to refresh the page.

If you need any other information let me know, and thanks for any help you can give me :)

async GetFriendsAsync() {
    while (true) {
      await new Promise(resolve => setTimeout(resolve, 1000));

      const response = await fetch("http://localhost:8080/api/getfriends", {
        method: "POST",
        body: JSON.stringify({
          "ID": this.GetSessionValueNullSafe("ID")
        })
      }).then((response) => {
        return response.text();
      });

      let stringFriends = response.split("<[SePaRaToR]>");
      if (stringFriends[0] == '') {
        continue;
      }

      this.friends = stringFriends.sort((a, b) => {
        if (parseInt(JSON.parse(a).ID) > parseInt(JSON.parse(b).ID)) {
          return 1;
        }
        else if (parseInt(JSON.parse(a).ID) < parseInt(JSON.parse(b).ID)) {
          return -1;
        }
        else {
          return 0;
        }
      });
    }
  }
async GetMessagesAsync() {
    while (true) {
      await new Promise(resolve => setTimeout(resolve, 1000));
      
      if (this.selectedFriendString == '') {
        continue;
      }

      const response = await fetch("http://localhost:8080/api/getmessages", {
        method: "POST",
        body: JSON.stringify({
          "userID": this.GetSessionValueNullSafe("ID"),
          "friendID": JSON.parse(this.selectedFriendString).ID
        })
      }).then((response) => {
        if (response.ok) {
          return response.text();
        }
        else {
          alert("Something went wrong when trying to get messages!");
          return "";
        }
      });

      this.messages = response.split("<[SePaRaToR]>");
    }
  }
async GetFriendRequestsAsync() {
    while (true) {
      await new Promise(resolve => setTimeout(resolve, 1000));

      const response = await fetch("http://localhost:8080/api/getfriendrequests", {
        method: "POST",
        body: JSON.stringify({
          "ID": this.GetSessionValueNullSafe("ID")
        })
      }).then((response) => {
        return response.text();
      });

      if (response.split("<[SePaRaToR]>")[0] == '') {
        this.friendRequests = [];
        continue;
      }

      let requests = response.split("<[SePaRaToR]>").map((item) => { return JSON.parse(item); });
      let friend_requests = [];

      for (let i = 0; i < requests.length; i++) {
        const response = await fetch("http://localhost:8080/api/getuser/id", {
          method: "POST",
          body: JSON.stringify({
            "ID": requests[i].senderID
          })
        }).then((response) => {
          return response.json();
        });

        friend_requests.push(JSON.stringify(response));
      }

      friend_requests = friend_requests.sort((a, b) => {
        if (parseInt(JSON.parse(a).ID) > parseInt(JSON.parse(b).ID)) {
          return 1;
        }
        else if (parseInt(JSON.parse(a).ID) < parseInt(JSON.parse(b).ID)) {
          return -1;
        }
        else {
          return 0;
        }
      });
      this.friendRequests = friend_requests;
    }
  }
async HeartbeatAsync() {
    var response: string;

    while (true) {
      await new Promise(resolve => setTimeout(resolve, 10000));

      response = await fetch("http://localhost:8080/api/heartbeat", {
        method: "POST",
        body: JSON.stringify({
          "ID": this.GetSessionValueNullSafe("ID")
        })
      }).then((response) => {
        return response.text();
      });
    }
  }
async GetUserOnlineState(userID: string) {
    var response = await fetch("http://localhost:8080/api/getuseractivestate", {
      method: "POST",
      body: JSON.stringify({
        "ID": userID
      })
    }).then((response) => {
      return response.json();
    });

    let state = response.active === "1";
    response = null;
    return state;
  }

Above are all the async functions that are iterated. GetUserOnlineState() is called by the html template here:

<div *ngIf="GetUserOnlineState(Json.parse(friend).ID)" class="online-icon"></div>
<div *ngIf="!GetUserOnlineState(Json.parse(friend).ID)" class="offline-icon"></div>

When I comment out the HTML above the memory usage is relatively stable, its only when GetUserOnlineState() is being called that the memory usage increases continuously.



Solution 1:[1]

Thanks to @HenrikBøgelundLavstsen for explaining why this was happening :)

I fixed the issue by calling the GetUserOnlineState() function in a loop like the others, rather than calling it from the template. This fixed the issue with memory usage.

As for the HttpClient approach, I will look into this at it does seem like a better approach, but for now this has fixed the issue. Thanks for your help @HenrikBøgelundLavstsen and @MikeOne :)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Nathcat