如何从Angle的Observable / http / async调用返回响应?


110

我有返回一个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!
  }
}

我检查了如何从异步调用返回响应?发布但找不到解决方案


2
强调不可能将异步操作转换为同步一这一事实将是一个很好的观点。
n00dl3

@ n00dl3感谢您的提示!我试图在“您不应该做什么:”部分中进行解释。随时进行编辑。
eko

这回答了你的问题了吗?如何从异步调用返回响应?
异端猴

@HereticMonkey该帖子已经在答案中记入
eko

@eko和?这是否使问题少了重复?
异端猴

Answers:


117

原因:

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

因此,让我们看一下http调用。

this.es.getEventList()

实际发出(“触发”)请求后,与您的http请求subscribe等待响应。在等待期间,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分钟。


建议阅读:

这个答案的原始功劳是:如何返回异步调用的响应?

但是随着angular2版本的推出,我们被引入了打字稿和可观察对象,因此该答案有望涵盖使用可观察对象处理异步请求的基础。


3
当一个问题是通过在文章的确切同时提问回答。这是一个很好的地方停下来,写在博客上,而不是
乔纳斯Praem

3
@JonasPraem是的,但是分享关于Stackoverflow的知识没有错。如您所见,票数确实对这里的许多人有所帮助,并且它将继续这样做。
阿米特·奇加达尼

11

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

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


3

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

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

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

因此,如果将其记录在subscription块内,则它将正确记录响应。

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

3

这里的问题是,当您刚完成块外时,您正在初始化this.myEventssubscribe()哪个异步块中。因此在初始化之前被调用。console.log()subscribe()console.log()this.myEvents

请同时将console.log()代码移到subscribe()内,然后完成。

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

2

结果不明确,因为角度过程异步。您可以尝试如下:

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

1

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

1

未定义,因为此处的值是在通过预订服务调用中的服务设置任何数据之前记录的。因此,您必须等到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)
  }

在myEvents变量中设置数据时,在此处的subscription方法内创建控制台日志。

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

0

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

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');
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.