简体   繁体   中英

Calling http request from success of previous http response (avoiding callback hell) in Angular 4

I'm working with an application where I need to call an HTTP response to the success of an HTTP call. How should I refactor my code to avoid callback hell

My current code is:

Component.ts
SomeFunction() {
    const param2 = {ind_1: value_1, ind_2: value_2};
    this.service.callFunction1(param)
        .subscribe(
        f1_res => {
           if (f1_res.status === 'success' ) {
               this.service.callFunction2(f1_res)
                    .subscribe(
                        f2_res => {
                            this.service.callFunction3(f2_res)
                                .subscribe(
                                     f3_res => console.log('done', f3_res),
                                     f3_err => console.log(err)
                                 );
                        },
                        f2_err => console.log(f2_err)
                    )
           }
        },
        f1_err => console.log(err)
    );

}

And my service is pretty standard angular service

Service.ts
callFunction1(param) {
     return this.http.post<any>( 'someurl.com', param );
}

callFunction2(param) {
     return this.http.post<any>( 'someurl.com', param );
}

callFunction3(param) {
     return this.http.post<any>( 'someurl.com', param );
}

I understand this is bad, but how do I refactor this?

This is a simple case for a mergeMap chain. Something like this:

this.service.callFunction1(params).pipe(
  mergeMap(res => this.service.callFunction2(res)),
  mergeMap(res => this.service.callFunction3(res)),
)
  .subscribe(
    (result) => console.log('done', result),
    (error) => console.log('error in chain', error),
  );

Here's a working Stackblitz .

Edit: of course, you can wrap those response.status === 'success' checks in a function, or right there, but if you are simply checking for HTTP errors, they'll all simply break the chain in that error handler at the bottom.

function doStuff(param) {
    this.service.callFunction1(param)
        .subscribe().then(step1(param)
            .flatMap((step1Result) => step2(step1Result)
                .flatMap((step2Result) => step3(step2Result))
                .map((step3Result) => {
                    let doStuffResult = step3Result;
                    if (doStuffResult !== null) {
                        return doStuffResult;
                    }
                })));
}

function step1(f1_res) {
    let result: any = null;
    this.service.callFunction1(f1_res)
        .subscribe(res => {
            if (res.status === 'success') {
                // ...
                this.result = res;
            }
        }, error => {
            console.log(error);
        });

    return Rx.Observable.of(result);
}

function step2(f2_res) {
    let result = null;
    this.service.callFunction2(f2_res)
        .subscribe(
            f2_res => {
                if (f2_res.status === 'success') {
                    this.result = f2_res;
                }
            }, error => { console.log(error) });
    // ...
    return Rx.Observable.of(result);
}

function step3(f3_res) {
    let result = null;
    this.service.callFunction2(f3_res)
        .subscribe(
            f3_res => {
                this.result = f3_res;
            }, error => { console.log(error) });
    // ...
    return Rx.Observable.of(result);
}

One very elegant way of doing this is giving each subscribed call as its own single unit of responsibility and creating a new Observable so each of the unit still behaves individual yet they are triggered in a chain.

you could reduce the code a bit by using RxJS's operators (you still have callbacks though...).

eg your code could be transformed to something like this:

SomeFunction() {
const param2 = {ind_1: value_1, ind_2: value_2};
this.service.callFunction1(param)
  .flatMap(f1_res => f1_res.status === 'success' ? this.service.callFunction2(f1_res) : Observable.throw("f1 error"))
  .flatMap(f2_res => this.service.callFunction3(f2_res))
  .subscribe(
    f3_res => console.log('done', f3_res),
    error => console.error(error)
    );
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM