Angular 中多个 HTTP 请求的最佳方式

Pra*_*avy 3 javascript rxjs typescript angular

我正在尝试一一发送 2 个 HTTP 请求;如果第一个成功,则发送第二个,如果不成功,则显示关于第一个请求的相应错误消息。

我打算使用类似的东西,但不确定它是否是这种情况的最佳选择:

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: 'app/app.component.html'
})
export class AppComponent {
  loadedCharacter: {};
  constructor(private http: HttpClient) {}

  ngOnInit() {
    this.http.get('/api/people/1').subscribe(character => {
      this.http.get(character.homeworld).subscribe(homeworld => {
        character.homeworld = homeworld;
        this.loadedCharacter = character;
      });
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

我有不同的请求,例如 PUT 和 CREATE 也使用这种方法。我知道还有其他方法,例如forkjoin, mergemap,但是如果这个方法解决了我的问题,则似乎更具可读性。任何的想法?

Yev*_*iuk 8

首先,您的代码可以正常工作,这很棒 - 您可以保持原样,一切都会好起来的。

另一方面,有一种方法可以进行多项改进,以帮助您和您的同事在未来:

  1. 尝试将与 http 相关的逻辑移动到服务中,而不是在组件中调用 http - 这将帮助您将代码拆分为与视图相关的逻辑和与业务/获取/转换相关的逻辑。
  2. 尽量避免嵌套subscribes - 您不仅会忽略Observables的强大功能,而且还会将代码绑定到某个流程,而无法在应用程序中的某处重用这些行。返回Observable可能会帮助您“共享”请求的结果或以某种方式对其进行转换。
  3. flatMap/mergeMapconcatMapswitchMap以不同的方式工作,使您能够以自己想要的方式控制行为。尽管如此,因为http.get()它们的工作方式几乎相似,所以尽快开始学习那些组合运算符是个好主意。
  4. 想想在这种情况下您将如何处理错误 - 如果您的第一次调用会导致错误,会发生什么?Observables 有一个强大的机制来处理它们,同时只.subscribe允许你以一种方式处理错误。

使用 的示例switchMap

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: 'app/app.component.html'
})
export class AppComponent {
  loadedCharacter: {};
  constructor(private http: HttpClient) {}

  ngOnInit() {
    const character$ = this.http.get('/api/people/1').pipe(
      tap(character => this.characterWithoutHomeworld = character), // setting some "in-between" variable
      switchMap(character => {
        return this.http.get(character.homeworld).pipe(
            map(homeworld => {
                    return {
                        ...character,
                        homeworld: homeworld
                    }
                }
            )
        )
      }),
      catchError(errorForFirstOrSecondCall => {
        console.error('An error occurred: ', errorForFirstOrSecondCall);
        // if you want to handle this error and return some empty data use:
        // return of({});
        
        // otherwise: 
        throw new Error('Error: ' + errorForFirstOrSecondCall.message);
      })
);

    // you can either store this variable as `this.character$` or immediately subscribe to it like:
    character$.subscribe(loadedCharacter => {
        this.loadedCharacter = loadedCharacter;
    }, errorForFirstOrSecondCall => {
       console.error('An error occurred: ', errorForFirstOrSecondCall);
    })
  }
}


Run Code Online (Sandbox Code Playgroud)