如何从角度调用Observable / http / async调用返回响应?

时间:2017-03-27 20:17:15

标签: javascript angular asynchronous typescript observable

我有一个服务,它返回一个observable,它向我的服务器发出一个http请求并获取数据。我想使用这些数据,但我总是得到undefined。有什么问题?

服务

@Injectable()
export class EventService {

  constructor(private http: Http) { }

  getEventList(): Observable<any>{
    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });

    return this.http.get("http://localhost:9999/events/get", options)
                .map((res)=> res.json())
                .catch((err)=> err)
  }
}

组件:

@Component({...})
export class EventComponent {

  myEvents: any;

  constructor( private es: EventService ) { }

  ngOnInit(){
    this.es.getEventList()
        .subscribe((response)=>{
            this.myEvents = response;
        });

    console.log(this.myEvents); //This prints undefined!
  }
}

11 个答案:

答案 0 :(得分:85)

<强>原因:

它是undefined的原因是您正在进行异步操作。这意味着完成getEventList方法需要一些时间(主要取决于您的网络速度)。

让我们看一下http电话。

this.es.getEventList()

在您使用subscribe实际制作(“触发”)您的http请求后,您将等待以获得响应。在等待时,javascript将执行此代码下面的行,如果遇到同步赋值/操作,它将立即执行它们。

所以订阅getEventList()并等待回复后,

console.log(this.myEvents);

行将立即执行。在响应从服务器到达之前,它的值是undefined(或者首先是你初始化它的任何内容)。

类似于:

ngOnInit(){
    setTimeout(()=>{
        this.myEvents = response;
    }, 5000);

    console.log(this.myEvents); //This prints undefined!
}

<小时/> 的解决方案:

  

那么我们如何克服这个问题呢?我们将使用回调函数,即subscribe方法。因为当数据从服务器到达时,它将位于带有响应的subscribe内。

所以将代码更改为:

this.es.getEventList()
    .subscribe((response)=>{
        this.myEvents = response;
        console.log(this.myEvents); //<-- not undefined anymore
    });

会在一段时间后打印回复。

<小时/> 你应该做什么:

除了记录之外,您的回复可能还有很多事情要做;当数据到达时,你应该在回调内部(subscribe函数内)执行所有这些操作。

另一件需要提及的事情是,如果您来自Promise背景,则then回调与subscribe对应的可观察对应。

<小时/> 你不该做的事情:

您不应该尝试将异步操作更改为同步操作(不是您可以)。我们具有异步操作的原因之一是不让用户等待操作完成,而他们可以在该时间段内执行其他操作。假设您的一个异步操作需要3分钟才能完成,如果我们没有异步操作,接口将冻结3分钟。

建议阅读:

这个答案的最初归功于:How do I return the response from an asynchronous call?

但是对于angular2版本,我们引入了typescript和observables,所以这个答案希望涵盖处理带有observables的异步请求的基础知识。

答案 1 :(得分:8)

在angular / javascript中进行http调用是异步操作。 因此,当您进行http调用时,它将分配新线程来完成此调用并开始执行下一行与另一个线程。 这就是为什么你得到未定义的价值。 所以在下面进行更改以解决此问题

this.es.getEventList()  
      .subscribe((response)=>{  
       this.myEvents = response;  
        console.log(this.myEvents); //<-this become synchronous now  
    });

答案 2 :(得分:3)

如果仅在模板中使用myEvents,则可以使用WebSocket messages are not

此处使用asyncPype和Angular4 HttpClient asyncPype

的示例

答案 3 :(得分:1)

这里的问题是,您刚从this.myEvents块开始执行subscribe()时,正在将console.log()初始化为subscribe(),这是一个异步块。 因此console.log()this.myEvents初始化之前被调用。

请将您的console.log()代码也移到subscribe()中,您就完成了。

ngOnInit(){
    this.es.getEventList()
        .subscribe((response)=>{
            this.myEvents = response;
            console.log(this.myEvents);
        });
  }

答案 4 :(得分:0)

还要确保将响应映射到json输出。否则它将返回纯文本。你这样做是这样的:

getEventList(): Observable<any> {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });

return this.http.get("http://localhost:9999/events/get", options)
            .map((res)=>{ return res.json();}) <!-- add call to json here
            .catch((err)=>{return err;})
}

答案 5 :(得分:0)

可观察对象是懒惰的,因此您必须订阅才能获得该值。您已经在代码中正确地订阅了它,但同时在“ subscribe”块之外记录了输出。这就是为什么它是“未定义的”。

ngOnInit() {
  this.es.getEventList()
    .subscribe((response) => {
        this.myEvents = response;
    });

  console.log(this.myEvents); //Outside the subscribe block 'Undefined'
}

因此,如果将其记录在订阅块中,它将正确记录响应。

ngOnInit(){
  this.es.getEventList()
    .subscribe((response)=>{
        this.myEvents = response;
        console.log(this.myEvents); //Inside the subscribe block 'http response'
    });
}

答案 6 :(得分:0)

由于角度过程async,结果是不确定的。 您可以尝试如下操作:

prediction = model.predict(test_X)
probs = prediction.max(1)

答案 7 :(得分:0)

未定义,因为此处的值是在通过上述订阅服务调用中的服务设置任何数据之前记录的。因此,您必须等到ajax调用完成并从响应数据中设置数据。

getEventList(): Observable<any>{
    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });

    return this.http.get("http://localhost:9999/events/get", options)
                .map((res)=> res.json())
                .catch((err)=> err)
  }

在控制台方法中将控制台日志设置为subscription方法,当在myEvents变量中设置数据时,该方法将创建日志。

ngOnInit(){
    this.es.getEventList()
        .subscribe((response)=>{
            this.myEvents = response;
     // This prints the value from the response
    console.log(this.myEvents)
        }); 
  }

答案 8 :(得分:0)

您可以使用以下方法从服务返回值

以下是服务:

 export class AppServiceService {
    
      constructor(private http: HttpClient) { }
    
      getStudentDetails(): Observable<Student[]>{
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });
        return this.http.get<Student[]>('http://localhost:8080/api/student',options);
    
      }
    
    }

在组件中,您可以使用订阅方法来加载数据,如下所示

export class AppComponent {
  
  title = 'angular-demo';

  constructor(private appService: AppServiceService) { }

   studentDetails: Student[];

  ngOnInit(): void {
    this.appService.getStudentDetails().subscribe(data => {
      this.studentDetails=data;
    },error=>{
         console.log(error);
    });

  }
}

更多参考请参考以下网址:

https://beginnersbug.com/call-http-rest-service-from-angular/

答案 9 :(得分:0)

要做到这一点,您有两个选择:

假设我们有一个返回送货详情数组的服务:

  getShippingPrices(): Observable<IShippingDetails[]> {
    return this.http.get<IShippingDetails[]>('/assets/shipping.json');
  }

1.使用异步管道:当您只想在模板中显示结果时的简单方法

在组件类中直接将observable赋值给变量:

export class ShippingComponent implements OnInit {
  shipOptions1 = this.cartService.getShippingPrices();
  constructor(private cartService: CartService) {}

  ngOnInit() {}
}

然后在模板中使用异步管道:

<div *ngFor="let s of shipOptions1 |async">
  <label>{{s.type}}</label>
</div>

参考:检查此 URL 中的第 4 点 https://angular.io/start/start-data#configuring-the-shippingcomponent-to-use-cartservice

2.使用订阅:当您想要操作它或想要在/从响应中执行一些业务逻辑时

export class ShippingComponent implements OnInit {
  shipOptions2: IShippingDetails[] = [];
  constructor(private cartService: CartService) {}

  ngOnInit() {
    this.cartService.getShippingPrices().subscribe(response => {
      this.shipOptions2 = response;
      //console.log(this.myEvents);
      //All other code using shipOptions2
    });
  }
}

答案 10 :(得分:-1)

您可以尝试这种方法 -

let headers = new Headers({'Accept': 'application/json'});
let options = new RequestOptions({headers: headers});

return this.http
    .get(this.yourSearchUrlHere, options) // the URL which you have defined
    .map((res) => {
        res.json(); // using return res.json() will throw error
    }
    .catch(err) => {
        console.error('error');
    }