1

I have method in class that load retrieve information from server:

public getClassesAndSubjects(school: number, whenDate: string) {
    this.classService.GetClassesAndSubjects(school, whenDate).subscribe(data => {
      if (!data.hasOwnProperty('errors')) {
        this.classesSubjects = data;

      }
    }, error => {
      console.log("ERROR loading GetClassesAndSubjects: " + error);
    });
  }

In successful result it fills object: this.class Subjects.

There is another method then returns this data:

public getClasses() {
    return this.classesSubjects;
  }

So, then I use this consistently:

let a = new ClassObj();
a.getClassesAndSubjects(1,'2018-01-01');
a.getClasses();

When I call a.getClasses(); it returns empty object because previous method is not give response from server.

1
  • I can put a.getClasses(); inside: .subscribe(data => {} but I need call this outside Commented Apr 11, 2018 at 13:07

3 Answers 3

2

Short answer:

Let getClassesAndSubjects return an Observable. Whenever you need to value stored in classesSubjects, subscribe to getClassesAndSubjects().

However, while it will syntactically work, it may not be the best option. The best option, depends. :-)

Long answer:

The problem is due to async code execution.

In JavaScript/TypeScript, there are a couple of ways to deal with these things. Which option to take, depend on your needs:

I highly recommend reading (and trying) tutorials on these topics BEFORE fixing your code. Investing time upfront to understand these prerequisite concepts will save you lots of bug fixing/debugging time in the future.

Sign up to request clarification or add additional context in comments.

4 Comments

Sound advice. For 95% of cases async/await backed by Promises leads to straightforward, maintainable, and highly readable code, while Observables, powerful as they are, are frankly unnecessary. In some projects I've added a SimpleHttpClient that is about 6 lines of code that delegates to the underlying Angular HttpClient.
I updated my answer to contrast the async/await approach. Ironically, it not only works well, but invalidates the inception of the question itself.
Man my observer is called twice
@dooglu there's no code in this answer so I don't see how it's possible that the author could be responsible. If you use the code that I wrote in my answer then I can promise you it won't cause double subscription
1

You should generalize getClassesAndSubjects adjusting it to return a value, in this case an Observable. Instead of having it call subscribe, it will now be composable.

getClassesAndSubjects(school: number, whenDate: string) {
  return this.classService.getClassesAndSubjects(school, whenDate)
    .map(data => {
      if ('errors' in data) { // type guard
        throw Error(data.errors);
      }
      return data;
    });
}

const c = new ClassObj();

c.getClassesAndSubjects(1,'2018-01-01').subscribe(classesAndSubjects => {
  this.classesAndSubjects = classesAndSubjects;
}, handleError);


// outside of the class!
export function handleError(error: any) {
  console.log("ERROR loading GetClassesAndSubjects: " + error);
}

Note that getClasses was a pointless method, it may as well not exist and if it were to, a get property would be cleaner. Also, it has a most confusing name in the context of its sister method getClassesAndSubjects given what it returns. So I just removed the method.

Addendum:

async/await having been brought up and being an elegant way to lift writing asynchronous programming from the tedium and unmaintainability of callbacks, I'll just show how this would work with async/await over standard Promises.

import 'rxjs/add/operator/toPromise';
// ...

async getClassesAndSubjects(school: number, whenDate: string) {
  const data = await this.classService.getClassesAndSubjects(school, whenDate)
    .toPromise();
  if ('errors' in data) { // type guard
    throw Error(data.errors);
  }
  return data;
}

// ...

const c = new ClassObj();

try {
  this.classesAndSubjects = await this.getClassesAndSubjects(1,'2018-01-01');
}
catch (e) {
  handleError(e);
}

The advantages are strong, being able to use standard syntactic constructs without any adjustments, maintaining the clarity of linear control flow over, and above all dealing with regular values and exceptions the whole way down. There is no need for the subscribe vs return dilemma with this approach as everything is compositional. In that way we sidestep the entire reason for the question. Sure it's less powerful, but its also simpler.

Comments

1

getClassesAndSubjects performs an asynchronous operation, so, when you call getClasses the operation has not yet finished. You need to return an observable or a promise from getClassesAndSubjects:

public getClassesAndSubjects(school: number, whenData: string): Observable<your-type> {
    const observable = this.classService.GetClassesAndSubjects(school, whenDate);
    observable.subscribe(data => {
      if (!data.hasOwnProperty('errors')) {
        this.classesSubjects = data;

      }
    }, error => {
      console.log("ERROR loading GetClassesAndSubjects: " + error);
    });

    return observable;
}

Now:

a.getClassesAndSubjects(1,'2018-01-01').subscribe(value => {
     a.getClasses();
});

If a function performs asynchronous operations, any action that depends of the results of such operation, must take into account that, and wait for the operation to complete.

You can also use async/await. In this case, the asynchronous function must return a Promise:

public async getClassesAndSubjects(school: number, whenData: string): Promise<your-type> {
    const observable = this.classService.GetClassesAndSubjects(school, whenDate);
    observable.subscribe(data => {
      if (!data.hasOwnProperty('errors')) {
        this.classesSubjects = data;

      }
    }, error => {
      console.log("ERROR loading GetClassesAndSubjects: " + error);
    });

    return observable.toPromise();
}

And now, wherever you want to use it:

async function whatever() {
    // ...
    await a.getClassesAndSubjects(1, '2018-01-01');
    a.getClasses();
}

By doing this, the execution of function whatever gets suspended until the promise returned by a.getClassesAndSubjects is fulfilled or rejected, so, when a.getClasses is executed, the data is there. Of course, the fact that this function gets 'suspended' does not mean the application get suspended, too. What happens is that, under the hood, the async function gets broken into parts, and the second part (after the await) gets executed inside a Promise.then method. But the compiler doest that for you, so the solution is much more elegant.

You need to remember that any function in which you use await must be declared as async.

8 Comments

Yes, I realize that, you recommend to add observer to getClassesAndSubjects and subscribe on this?
Yes. In fact, is the only solution. Well, you could use async/await, but in essence does exactly the same, only a bit more elegantly. I can add that to my answer
@OscarPaz I want to like this, but the anys are killing me. Just leave the type off and let the compiler infer it, especially when not knowing anything better from the context (question)
You can remove the anys and put yourself the types, or let the compiler infer them, if it is possible. I put any in the examples just because I don't know the types, not because I like any at all.
If it irks you so much, I changed the any for the placeholder your-type :-)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.