2

What is the way to return result from the service directly, without returning Observable and then using then clause. I searched for many things, such as using pipe,of,take,toPromise,map,async-await but non of them return result on service call. Also, some of the clauses are not available on current version of rxjs. Can you please help?

Note I have condition here where if api has not worked, I get it from local.

@Injectable()
export class SomeService {
  GetDataEitherFromApiOrFromLocalStotage():Any 
  {
    let result;
    this.http.get("https://"+ this.url +"/api/main/apidata").subscribe(
       next=>{result=next;},
       error => {result=this.localdata();},  
       ()=>{return result;}
    )
  }
}

when calling the function (which returns null, with async-await also)

@Component()
export class SomeComponent implements OnInit {

  constructor(private service:SomeService) {}

  ngOnInit() {
   let data = this.service.GetDataEitherFromApiOrFromLocalStotage();
  }
}

UPDATE

The reason I'am trying to do this is because I want to put all the logic inside the Service, and not in Component.

InGeek
  • 2,532
  • 2
  • 26
  • 36
  • 3
    The Angular HTTP service is inherently asynchronous. It sounds like you want a blocking HTTP call, and Angular won't do that. You can do a low-level JavaScript XMLHttpRequest in a way that blocks until completion, but this is both deprecated and a bad idea. – kshetline Oct 15 '19 at 13:52
  • So there is no built-in way to enforce waiting, at least for some services? – InGeek Oct 15 '19 at 13:58
  • 2
    The short answer is no. Either add logic within a pipe or subscribe. You'd be better off describing what you want to achieve and we can give you the Observable way of doing things (join us and become *one of us ... one of us*) – Andrew Allen Oct 15 '19 at 14:07
  • Thanks. The reason I'am trying to do this is because I want to put all the logic inside the Service, and not in Component. The logic might get bigger, and I don't want to load the component. And that's so strange that rxjs doen't provide this option. @AndrewAllen how can it be accomplished with `pipe`? – InGeek Oct 15 '19 at 14:13
  • 1
    If what you mean is that you have a component that shouldn't be instantiated before this service is completely ready, you can do something like ``. – kshetline Oct 15 '19 at 14:17
  • 1
    Thanks. Specifically, return result from the service: try api first in case if fails return hardcoded data. – InGeek Oct 15 '19 at 14:22

1 Answers1

3

You could try something like this:

I've included both the promise route (at the bottom) and the observable route

Isolate your api / data engineering in the service - components shouldn't care how you get it.

GetDataEitherFromApiOrFromLocalStorage(): Observable<any> {
    return this.http.get('https://' + this.url + '/api/main/apidata').pipe(
        catchError((error) => {
            // Evaluate if error is fatal or expected
            errorIsNotFatal = true;
            if (errorIsNotFatal) {
                return this.localdata(); // this should return an observable;
            }
            return throwError(error);
        })
    );
}

Take note of this portion: catchError() will either return a true error (if its fatal) or get it from local if its an expected error.

        catchError((error) => {
            // Evaluate if error is fatal or expected
            errorIsNotFatal = true;
            if (errorIsNotFatal) {
                return this.localdata(); // this should return an observable;
            }
            return throwError(error);
        })

For this to work localData() also needs to return an observable:

localdata(): Observable<any> {
    return of('anything or any kind of data');
}

Finally in your component, all you have to do is subscribe() and wait for the data to come back:

export class SomeComponent implements OnInit {
    public data: any;

    constructor(private service: SomeService) {
    }

    ngOnInit() {
        this.service.GetDataEitherFromApiOrFromLocalStorage().subscribe((response) => {
            this.data = response;
        });
    }
}

FINALLY - If a promise is your requirement then you can always do this and roll your own promise. This will allow you to call then, however I'd discourage this and suggest using the observable pattern instead:

GetDataEitherFromApiOrFromLocalStorage(): Promise<any> {
    return new Promise((resolve, reject) => {
        this.http.get("https://" + this.url + "/api/main/apidata").subscribe(
            (next) => { resolve(next); },
            (error) => { reject(error); }
        );
    });
}
Damian C
  • 2,111
  • 2
  • 15
  • 17
  • 2
    @InGeek this is how I would do it and what I was alluding to (other than the promise part - see [why I'll never use promises in an angular app](https://stackoverflow.com/a/58321070/4711754)). You can also inject a "local data getter" service in this service. shadowfox476 can I suggest less bold? – Andrew Allen Oct 15 '19 at 14:32