title: Angular 7 - forEach iteration inside of a promise is executing after the promise resolves. Why?
body: <p>I created a service for some manipulation that needs to occur before prior to calling the drawPoll() function. I added console logs to keep track of the execution order and cannot figure out why the function chained to .then() is executing BEFORE the forEach iteration inside of the promise has finished. The whole point of creating the service and wrapping the forEach manipulation in the promise was so that I could be absolutely certain that the forEach iteration has completed before calling the drawPoll() function. What am I missing here? </p>
  
  <p><strong>poll.component.ts</strong></p>
  
  <pre><code>import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
  import * as Chart from 'chart.js';
  import { Observable } from 'rxjs';
  import { FirebaseService } from '../services/firebase.service';
  import { first } from 'rxjs/operators';
  import { CardModule } from 'primeng/card';
  import { AngularFireAuth } from '@angular/fire/auth';
  
  import nflPollTypes from '../../assets/types/poll-types-nfl.json';
  import nflScoringTypes from '../../assets/types/scoring-types-nfl.json';
  
  @Component({
    selector: 'app-poll',
    templateUrl: './poll.component.html',
    styleUrls: ['./poll.component.scss']
  })
  export class PollComponent implements OnInit {
    chart:any;
    poll:any;
    votes:[] = [];
    labels:string[] = [];
    title:string = "";
    isDrawn:boolean = false;
    inputChoices:any = [];
    username:string = "";
    points:number;
    uid:string = "";
    votedChoice:string;
    hasVoted:boolean = false;
    scoringTypeString:string;
    nflPollTypes:any = nflPollTypes.types;
    nflScoringTypes:any = nflScoringTypes.types;
  
    @Input()
    pollKey: string;
  
    @Input()
    pollDocument:any;
  
    @Output()
    editEvent = new EventEmitter&lt;string&gt;();
  
    @Output()
    deleteEvent = new EventEmitter&lt;string&gt;();
  
    constructor(private firebaseService: FirebaseService, private afAuth: AngularFireAuth) { }
  
    ngOnInit() {
      const pollData:any = this.pollDocument.payload.doc;
      this.pollKey = pollData.id;
      this.poll = {
        id: this.pollKey,
        helperText: pollData.get("helperText"),
        pollType: pollData.get("pollType"),
        scoringType: pollData.get("scoringType"),
        user: pollData.get("user")
      };
  
      this.firebaseService.initPoll(this.pollKey, this.isDrawn, this.drawPoll).then((choices, votedChoice) =&gt; {
        this.poll.choices = choices;
        this.votedChoice = votedChoice;
        this.drawPoll();
      })
    }
  
    drawPoll() {
      console.log("DRAW!", this.poll);
      if (this.isDrawn) {
        this.chart.data.datasets[0].data = this.poll.choices.map(choice =&gt; choice.votes);
        this.chart.data.datasets[0].label = this.poll.choices.map(choice =&gt; choice.text);
        this.chart.update()
      }
      if (!this.isDrawn) {
        this.inputChoices = this.poll.choices;
        var canvas =  &lt;HTMLCanvasElement&gt; document.getElementById(this.pollKey);
        if(canvas) {
          var ctx = canvas.getContext("2d");
          this.chart = new Chart(ctx, {
            type: 'horizontalBar',
            data: {
              labels: this.poll.choices.map(choice =&gt; choice.text),
              datasets: [{
                label: this.title,
                data: this.poll.choices.map(choice =&gt; choice.votes),
                fill: false,
                backgroundColor: [
                  "rgba(255, 4, 40, 0.2)",
                  "rgba(19, 32, 98, 0.2)",
                  "rgba(255, 4, 40, 0.2)",
                  "rgba(19, 32, 98, 0.2)",
                  "rgba(255, 4, 40, 0.2)",
                  "rgba(19, 32, 98, 0.2)"
                ],
                borderColor: [
                  "rgb(255, 4, 40)",
                  "rgb(19, 32, 98)",
                  "rgb(255, 4, 40)",
                  "rgb(19, 32, 98)",
                  "rgb(255, 4, 40)",
                  "rgb(19, 32, 98)",
                ],
                borderWidth: 1
              }]
            },
            options: {
              events: ["touchend", "click", "mouseout"],
              onClick: function(e) {
                console.log("clicked!", e);
              },
              tooltips: {
                enabled: true
              },
              title: {
                display: true,
                text: this.title,
                fontSize: 14,
                fontColor: '#666'
              },
              legend: {
                display: false
              },
              maintainAspectRatio: true,
              responsive: true,
              scales: {
                xAxes: [{
                  ticks: {
                    beginAtZero: true,
                    precision: 0
                  }
                }]
              }
            }
          });
          this.isDrawn = true;
        }
      }
    }
  
  }
  </code></pre>
  
  <p><strong>firebase.service.ts</strong></p>
  
  <pre><code>import { Injectable } from '@angular/core';
  import { AngularFirestore } from '@angular/fire/firestore';
  import { map, switchMap, first } from 'rxjs/operators';
  import { Observable, from } from 'rxjs';
  import * as firebase from 'firebase';
  import { AngularFireAuth } from '@angular/fire/auth';
  
  @Injectable({
    providedIn: 'root'
  })
  export class FirebaseService {
    // Source: https://github.com/AngularTemplates/angular-firebase-crud/blob/master/src/app/services/firebase.service.ts
    constructor(public db: AngularFirestore, private afAuth: AngularFireAuth) { }
  
    initPoll(pollKey, isDrawn, drawPollCallback) : any {
      return new Promise((resolve, reject) =&gt; {
        let votedChoice;
        let choices = [];
        this.getChoices(pollKey).pipe(first()).subscribe(fetchedChoices =&gt; {
        fetchedChoices.forEach(choice =&gt; {
          const choiceData:any = choice.payload.doc.data();
          const choiceKey:any = choice.payload.doc.id;
          this.getVotes(choiceKey).pipe(first()).subscribe((votes: any) =&gt; {
            choices.push({
              id: choiceKey,
              text: choiceData.text,
              votes: votes.length,
              players: choiceData.players
            });
            let currentUserId = this.afAuth.auth.currentUser.uid;
            let hasVoted = votes.filter((vote) =&gt; {
              return (vote.payload.doc._document.proto.fields.choice.stringValue == choiceKey) &amp;&amp;
              (vote.payload.doc._document.proto.fields.user.stringValue == currentUserId);
            });
            if (hasVoted.length &gt; 0) {
              votedChoice = hasVoted[0].payload.doc._document.proto.fields.choice.stringValue;
            }
          });
          this.getVotes(choiceKey).subscribe((votes: any) =&gt; {
            if (isDrawn) {
              const selectedChoice = choices.find((choice) =&gt; {
                return choice.id == choiceKey
              });
              selectedChoice.votes = votes.length;
              drawPollCallback();
            }
          });
        });
        console.log("Done iterating");
      });
      resolve(choices, votedChoice)
      });
    }
  
  }
  </code></pre>