如何以角度返回可观察/http/异步调用的响应?

我有一个服务,返回一个可观察的,做一个 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!
}
}

我检查了 如何从异步调用返回响应?后,但无法找到一个解决方案

45751 次浏览

理由:

它是 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!
}

解决方案: > 那么我们如何克服这个问题呢?我们将使用回调函数,这是“订阅”方法。因为当数据从服务器到达时,它将在“订阅”中与响应一起。

所以把代码改成:

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

将打印的回应. . 在一段时间后。


你该做什么

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

另一件需要提到的事情是,如果您来自 Promise背景,那么 then回调对应于具有可观察值的 subscribe


不该做的事

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


建议阅读:

这个问题的原始答案归功于: 如何从异步调用返回响应?

但是在 angular2发行版中,我们介绍了类型脚本和可观测数据,所以这个答案有望涵盖处理带有可观测数据的异步请求的基础知识。

还要确保将响应映射到 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;})
}

使用 angle/javascript 进行 http 调用是异步操作。 因此,当您进行 http 调用时,它将分配新的线程来完成这个调用,并开始执行下一行与另一个线程。 这就是为什么你得到了不确定的价值。 所以做出以下改变来解决这个问题

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

您可以简单地尝试这种方法-

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');
}

如果只在模板中使用 myEvents,则可以使用 异步管

这里的示例使用的是 syncPipe 和 Angular4HttpClient例子

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

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'
});
}

这里的问题是,您正在将 this.myEvents初始化为 subscribe(),这是一个异步块,而在 subscribe()块之外执行 console.log()。 所以 console.log()this.myEvents初始化之前被调用。

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

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

结果是未定义的,因为角进程异步。 你可尝试以下方法:

async ngOnInit(){
const res = await this.es.getEventList();
console.log(JSON.stringify(res));
}

未定义,因为在从上面的订阅服务调用设置来自服务的任何数据之前,此处的值已被记录。因此,您必须等待 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)
}

在这里创建订阅方法中的 Console 日志,该方法在 myEvents 变量中设置数据时创建日志。

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

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

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

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

1. 使用 Async 管道 : 当你只想在模板中显示结果时,这是一种简单的方法

在组件类中,直接将可观测值赋给变量:

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. 使用 Subscribe : 当您想要操作它或者想要在响应上执行一些业务逻辑时

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
});
}
}