0

Im getting data of class People array with each person having attendance list as Observable<any[]>.

// Class person
class Person {
   id: number;
   name: string;
   attendance: Observable<any[]>; // An Observable array
}


// People array (I only use array for simplicity sake)
// NOTE: Every person has a list of attendance
people = Person[] = this.someService.getPeople(); 

Now I'm validating if some of the people may had 0 length of attendance assuming that attendance: Observable<any[]> is always an empty array [] if nothing is found, return false otherwise true to validateAttendance().

validateAttendance(): boolean {

    // Loop to each person and get attendance list.
    people.forEach(p => {
       // Get the list using a callback function.
       const func = (callback: (data) => void) => {
            // Get attendance using subscribe() method
            p.attendance.subscribe(list => {    
                callback(list);
            });
        }

       // An attempt to get the list from a callback function
       // Failed because return is undefined.
       const result = func(res => {
           console.log(res); // Got list of attendance of a given person.
           return res;
       });

       if (result.length <= 0) {
           alert(`${p.name} has no attendance, must be a lazy one please check.`);
           return false;
       }
    });

    return true;
}

In order to do that i attempted to create a callback function inside the validateAttendance() method and return the attendance list from it. HOWEVER the result variable is undefined!

It is possible to return a value from a callback function this way?

Shift 'n Tab
  • 8,808
  • 12
  • 73
  • 117
  • if ANY of the code inside `validateAttendance` is asynchronous (e.g. ` p.attendance.subscribe` looks asynchronous) then there is no way to return the result synchronously, no matter how much funky callbackery you add – Jaromanda X Dec 04 '17 at 04:17
  • @JaromandaX thanks i should really change approach, but i really thought we can return a value from `func(list => { return list });` – Shift 'n Tab Dec 04 '17 at 04:19
  • "return a value" ... sure, but what does `func` return? `undefined` ... – Jaromanda X Dec 04 '17 at 04:35
  • `func` was able to give me the list but when returning a value back to func yea it is `undefined`, sort of playing the flow haha – Shift 'n Tab Dec 04 '17 at 04:47
  • 1
    because asynchronous code remains asynchronous – Jaromanda X Dec 04 '17 at 04:48
  • 3
    Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Saravana Dec 04 '17 at 04:56
  • @Saravana the question is different, when you look at my code i created it from that source. and it doesn't solve my problem thanks to `@JaromandaX` – Shift 'n Tab Dec 04 '17 at 04:58

1 Answers1

1

There are several things wrong with your code:

  1. It is asynchronous, so the function needs to be async too (using Promise)
  2. func() does not return a value, so the result will always be undefined.
  3. You have alert() deep in your code. Are you going to pop up for each person with no attendance? It is better to separate logic and UI.

I have modified your code to the following:

function validateAttendance(people): Promise<boolean> {
  return getPeopleWithNoAttendance(people)
    .then(peopleWithNoAttendance => {
      if (peopleWithNoAttendance.length > 0) {
        // or loop though them
        alert(`${peopleWithNoAttendance[0].name} has no attendance, must be a lazy one please check.`);
        return false
      }
      return true
    })
}

function getPeopleWithNoAttendance(people) {
  return Promise.all(people.map(person => {
    return new Promise(r => {
      person.attendance.subscribe(r)
    }).then(attendance => {
      return attendance.length <= 0 ? person : undefined
    })
  })).then(listOfPeopleOrUndefined => {
    const listOfPeople = listOfPeopeleOrUndefined.filter(p => p)
    return listOfPeople
  })
}

Also changed is having the people variable passed in. If this is a method in a class, feel free to change that.

unional
  • 14,651
  • 5
  • 32
  • 56
  • good this is i really change my method to return Promise, it does great – Shift 'n Tab Dec 04 '17 at 06:55
  • 1
    `return new Promise(r => { person.attendance.subscribe(r) }` could be written more succintly as just `return person.attendance.toPromise()` – Duncan Dec 04 '17 at 09:46