从服务返回对象数组,直接从 observable

Returning object array from service, directly from observable

我对 TypeScript 和 RxJS 还很陌生,所以我对可观察对象和订阅的理解有限。

我需要从服务 class 调用一个方法,该方法 returns 一个对象数组(现在,它被记录到控制台),我将在单击按钮后的组件 class。

我 运行 遇到的问题是,当我尝试将数组分配给变量时,数组在控制台中返回为空。但是,当我直接记录 http.get 方法的结果时,我看到了我的对象数组。

我想知道我在这里忽略了什么会导致这种错误,我该怎么做才能修复它以避免 运行 再次出现这种情况?

cars.model.ts

export interface ICar {
    Make: string;
    Model: string;
    Year: string;
    Specifications: boolean[];
  }

cars.new.mock.json

[
    {
        "Make": "Honda",
        "Model": "CRV",
        "Year": "2021",
        "Specifications": [
            true,
            true,
            false,
            true,
            false
        ]
    },
    {
        "Make": "Toyota",
        "Model": "Camry",
        "Year": "2021",
        "Specifications": [
            true,
            true,
            false,
            true,
            false
        ]
    }
]

app.component.html

<h1>{{title}}</h1>

<button (click)="shownewCars()">New Cars</button>

app.component.ts

import { Component, OnInit } from '@angular/core';
import { CarsService } from './services/cars.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{

  title = 'AutoZone';

  constructor(private carsService: CarsService){
  }

  ngOnInit(): void {
  }
  
  shownewCars(){
    this.carsService.logNewCarsMessage();
    console.log(this.carsService.returnNewCars());
  }

}

cars.service.ts

import { HttpClient } from '@angular/common/http';
import { Injectable } from "@angular/core";
import { ICar } from '../models/cars.model';

@Injectable({
    providedIn: 'root'
})

export class CarsService{
    carsURL = '/assets/cars.mock.json';
    newCarsURL = '/assets/cars.new.mock.json'
    private newCars: ICar[] = [];

    constructor(private http : HttpClient){}
    
    returnNewCars(): ICar[]{
        this.http.get<ICar[]>(this.newCarsURL).subscribe(result => {this.newCars = result});
        return this.newCars;
    }

    logNewCarsMessage(){
        this.http.get(this.newCarsURL).subscribe((responseData) => console.log("Console log New Cars: ", responseData));
    }
}

控制台

[]
Console log New Cars: (2)[{...}, {...}]

您的问题与异步代码有关。

http.get(...) 函数 return 是一个 Observable<ICar[]>,订阅后将发送实际的 HTTP 请求。

这里的问题是代码没有等到 HTTP 响应从服务器返回。 .subscribe(result => {this.newCars = result}) 注册应在 HTTP 调用 return 秒后执行的回调函数,但无需实际等待即可立即继续。
下一行立即 return 是(在那一刻)仍然是空的 this.newCars 变量。

logNewCarsMessage 函数之所以起作用,是因为该函数仅在 HTTP 响应到达后才记录。正如您在控制台输出中所见,Console log New Cars: (2)[{...}, {...}] 行在 [] 之后打印 ,而您调用 logNewCarsMessage 函数 之前 returnNewCars.

有两种方法可以解决这个问题:使用 Promises,或者最好使用 Observables。

RxJS 可观察对象

不是直接 return 汽车数组,return 一个最终会发出汽车数组的 Observable

cars.service.ts

private newCars$: Observable<ICar[]>;

returnNewCars(): Observable<ICar[]> {
  this.newCars$ = this.http.get<ICar[]>(this.newCarsURL);
  return this.newCars$;
}

newCars$ 中的 $ 符号不是必需的,但它是一个很好的约定,表示该变量实际上是一个 Observable。

app.component.ts

shownewCars(): void {
  this.carsService.returnNewCars().subscribe(cars => console.log(cars));
}

承诺

您可以使用 .toPromise() 函数将 Observable 转换为 Promise。这样你就可以使用正常的 async/await 语法以同步的方式使用你的异步代码。
同样,我真的不推荐这种方式,因为虽然乍一看它可能看起来更容易,但它的功能要弱得多,而且 不是 Angular 或 RxJS 方式

cars.service.ts

private newCars: ICar[] = [];

async returnNewCars(): Promise<ICar[]> {
  this.newCars = await this.http.get<ICar[]>(this.newCarsURL).toPromise();
  return this.newCars;
}

app.component.ts

async shownewCars(): Promise<void> {
  const cars = await this.carsService.returnNewCars();
  console.log(cars);
}