首页 > 解决方案 > Angular 订阅从 API 返回的数据

问题描述

我正在尝试清理我的 Ionic 应用程序并将冗余功能移动到提供程序中。下面是我的大多数 API 函数的示例(为了简洁起见,我删除了很多不相关的东西)并且它们运行良好。

getDataFromApi() {
    ....
    Promise.all([
        ...
    ]).then((result) => {
        let headers = new Headers();
        ...
        let body = new FormData();
        ...
        this.http.post(url, body, headers)
            .map(res => res.json())
            .subscribe(data => {
                if (data == '{}') {
                    this.mydata = [];
                } else {
                    this.mydata = data;
                }
            }, error => { });
    });
}

所以我所做的就是将函数移动到提供程序中并像这样改变它

getDataFromApi() {
    ....
    Promise.all([
        ...
    ]).then((result) => {
        let headers = new Headers();
        ...
        let body = new FormData();
        ...
        return this.http.post(url, body, headers));
    });
}

然后在页面的构造函数中我这样称呼它

this.service.getDataFromApi()
    .map(res => res.json())
    .subscribe(data => {
        if (data == '{}') {
            this.mydata = [];
        } else {
            this.mydata = data;
        }
    }, error => { });

显然这是行不通的。我已经倾注了 2 天的 SO 帖子,也无法让其他人的示例和答案正常工作。我已经尝试在提供程序中进行映射并在页面中调用它时进行订阅,但无论我尝试什么,都会不断收到错误。我知道这个函数正在返回数据,因为我可以在将它移动到提供程序之前看到它。

我究竟做错了什么?

标签: angularionic-framework

解决方案


我认为最重要的是保持一致;你可以使用Promise 或只使用 observables来完成所有这些工作(而不是混合使用两者)。

请看一下这个 stackblitz 演示,在那里你可以看到如何只使用 observables 来发出类似的 HTTP 请求,以及只使用 Promise 来发出完全相同的请求。


使用可观察对象

您可以使用switchMap(或任何其他展平运算符)与fromPromise提供者以相同的方法处理所有这些:

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

// RxJS 
// Please notice that this demo uses the 5.5.2 version
import { Observable } from 'rxjs/Observable';
import { map } from 'rxjs/operators/map';
import { tap } from 'rxjs/operators/tap';
import { switchMap } from 'rxjs/operators/switchMap';

// ...

// Get a list of 5 users using observables
public getDataUsingObservables(): Observable<any> {
  const promises = Promise.all([
    this.somePromise('getDataUsingObservables', 1),
    this.somePromise('getDataUsingObservables', 2)
  ]);

  return fromPromise(promises)
    .pipe(
      switchMap(results => {
        console.log(`[getDataUsingObservables]: Both promises are finished`);
        const url = `https://randomuser.me/api/?results=5`;

        return this.http.get<any>(url);
      }),
      tap(res => {
        console.log(`[getDataUsingObservables]: The http request is finished`);
      })
  );
}

// Return a promise with the number sent as parameter 
private somePromise(callerMethod: string, aNumber: number): Promise<number> {
  console.log(`[${callerMethod}]: About to create a promise with the number ${aNumber}`);
  return Promise.resolve(aNumber);
}

然后你会像这样使用它:

this.dataService.getDataUsingObservables().subscribe(
  response => {
    this.responseUsingObservables = response;
  }, 
  error => {
    // Handle the error...
    alert(error);
  });

使用承诺

如果要使用 Promise,可以使用toPromise()操作符将 observable 转换为 Promise:

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

// RxJS 
// Please notice that this demo uses the 5.5.2 version
import { tap } from 'rxjs/operators/tap';

// ...

// Get a list of 5 users using promises
public getDataUsingPromises(): Promise<any> {
  const promises = Promise.all([
      this.somePromise('getDataUsingPromises', 1),
      this.somePromise('getDataUsingPromises', 2)
  ]);

  return promises
    .then(results => {
      console.log(`[getDataUsingPromises]: Both promises are finished`);
      const url = `https://randomuser.me/api/?results=5`;

      return this.http.get<any>(url)
        .pipe(
          tap(res => {
            console.log(`[getDataUsingPromises]: The http request is finished`);
          })
        )
        .toPromise();
    });
}

// Return a promise with the number sent as parameter 
private somePromise(callerMethod: string, aNumber: number): Promise<number> {
  console.log(`[${callerMethod}]: About to create a promise with the number ${aNumber}`);
  return Promise.resolve(aNumber);
}

然后你会像这样使用它:

this.dataService.getDataUsingPromises()
  .then(response => {
    this.responseUsingPromises = response;
  })
  .catch(error => {
    // Handle the error...
    alert(error);
  });

推荐阅读