angular/ts在异步调用中分配的全局变量在构造函数中未定义

angular/ts在异步调用中分配的全局变量在构造函数中未定义,angular,typescript,asynchronous,Angular,Typescript,Asynchronous,我正在使用对Web API的HTTP调用来检索2个API键以使用另一个API 通过2个函数检索这些API密钥: getApiKey和getAppId 当我在构造函数中调用这些函数时,它们返回的值(一个全局变量)是未定义的 当我在构造函数外部调用它时,它工作得很好 我不想使用全局变量,但当我尝试在getApiKey或getAppid函数体中创建变量并在http.get调用中赋值时,它也会返回未定义的变量 我猜这与http.get是异步的有关,但我不知道如何修复它/如何让它等待响应 这是我的密码:

我正在使用对Web API的HTTP调用来检索2个API键以使用另一个API

通过2个函数检索这些API密钥: getApiKey和getAppId

当我在构造函数中调用这些函数时,它们返回的值(一个全局变量)是未定义的

当我在构造函数外部调用它时,它工作得很好

我不想使用全局变量,但当我尝试在getApiKey或getAppid函数体中创建变量并在http.get调用中赋值时,它也会返回未定义的变量

我猜这与http.get是异步的有关,但我不知道如何修复它/如何让它等待响应

这是我的密码:

import { Component, OnInit } from '@angular/core';
import { Http, Headers, Response, RequestOptions } from '@angular/http';
import { Constants } from '../../utils/constants';
import { FormGroup, FormControl, FormBuilder, Validators } from '@angular/forms';

@Component({
  selector: 'app-recipes',
  templateUrl: './recipes.component.html',
  styleUrls: ['./recipes.component.css']
})
export class RecipesComponent {
  appid;  
  appkey;
  matchesList;  
  recipeSearchForm: FormGroup;
  notFoundError: boolean = false;

  constructor(private http: Http) {
    this.searchRecipeInit();    //undefined here

    this.recipeSearchForm = new FormGroup({
      recipeSearchInput: new FormControl()
    });
  }

  getApiKey(){
    this.http.get(Constants.GET_YUMMLY_APP_KEY, this.getOptionsSimple()).subscribe((res: Response) => {  
      this.appkey = res.text();
      console.log(this.appkey);   
    });
    return this.appkey;    
  }

  getAppId(){
    this.http.get(Constants.GET_YUMMLY_APP_ID, this.getOptionsSimple()).subscribe((res: Response) => {  
      this.appid = res.text(); 
      console.log(this.appid);      
    });
    return this.appid;    
  } 

  getSearchParams(){
    // get from search text field
    var str = this.recipeSearchForm.get('recipeSearchInput').value
    // split into words and add + in between
    if(str != null) {
      var correctFormat = str.split(' ').join('+');
      return correctFormat          
    }
    return str
  }

  getOptions(){
    var headers = new Headers();

    headers.append('Content-Type', 'application/json' );    
    headers.append('X-Yummly-App-Key',this.getApiKey());    
    headers.append('X-Yummly-App-ID',this.getAppId());

    let options = new RequestOptions({ headers: headers });

    return options;
  }

  getOptionsSimple(){
    var headers = new Headers();

    headers.append('Content-Type', 'application/json' ); 

    let options = new RequestOptions({ headers: headers });

    return options;
  }

  searchRecipe() {     
      // not undefined here
      this.http.get(Constants.GET_SEARCH_RECIPE+this.getSearchParams(), this.getOptions()).subscribe((res: Response) => {  
        this.matchesList = res.json().matches;

        console.log(this.matchesList);
        if(this.matchesList.length == 0){
          this.notFoundError = true;
        }
        else{
          this.notFoundError = false;
        }
      },
      (err) => {
        if(err.status == 400){
          // Bad Request
        }
        else if(err.status == 409){
          // API Rate Limit Exceeded
        }
        else if(err.status == 500){
          // Internal Server Error
        }
      });
  }

  searchRecipeInit() {     
    this.http.get(Constants.GET_SEARCH_RECIPE+"", this.getOptions()).subscribe((res: Response) => {  
      this.matchesList = res.json().matches;

      this.notFoundError = false;      
    },
    (err) => {
      if(err.status == 400){
        // Bad Request
      }
      else if(err.status == 409){
        // API Rate Limit Exceeded
      }
      else if(err.status == 500){
        // Internal Server Error
      }
    });
}
}

您包含的代码按预期工作。这个问题可能是重复的,但因为它更多的是关于可观测的,所以我会回答它

您对异步代码的误解是主要的问题,特别是像下面这样的代码

getAppId() {
    this.http.get(Constants.GET_YUMMLY_APP_ID, this.getOptionsSimple()) // 1
        .subscribe((res: Response) => {                                 // 
            this.appid = res.text();                                    // 3
            console.log(this.appid);                                    // 4
        });
    return this.appid;                                                  // 2
} 
代码按右侧标记的编号顺序执行。由于TypeScript/JavaScript是同步的,它将触发http.get。。。函数,然后继续下一行,在本例中是返回this.appid;。此时this.appid的值是多少?未定义,因此它按预期工作

class SearchRecipeDemo {

  private getAppId() {
    return this.http.get(Constants.GET_YUMMLY_APP_ID);
  }

  private getApiKey() {
    return this.http.get(Constants.GET_YUMMLY_APP_KEY);
  }

  private init(): void {
    this.searchRecipe();
  }

  private getOptions() {
    return Rx.Observable.zip(getApiKey(), getAppId()).map((result) => {
        // Prints your keys
        console.log(`apiKey: ${result[0].text()}`);
        console.log(`appId: ${result[1].text()}`);

        // Create the RequestOptions
        let headers = new Headers();

        headers.append('Content-Type', 'application/json');
        headers.append('X-Yummly-App-Key', result[0].text());
        headers.append('X-Yummly-App-ID', result[1].text();

          const options = new RequestOptions({
            headers: headers
          });
          return options;
        });
    });

  private searchRecipe() {
    this.getOptions().map((options) => {
        // Options here is the RequestOptions object returned from the 'getOptions' function
        console.log(options);

        //Make your request to search the recipe here
      })
      .subscribe();
  }
}

new SearchRecipeDemo().init();
您必须返回http.get…(在调用.subscribe函数之前不可用)的结果

由于您依赖于两个独立的http.get。。。调用,一个用于apiKey,一个用于appId,您可以利用Rx操作符等待它们完成/发出值。在本例中,我们想到的是Observable.zip函数

我已经创建了一个代码段,它将引导您正确地使用代码,使您的代码按预期工作

class SearchRecipeDemo {

  private getAppId() {
    return this.http.get(Constants.GET_YUMMLY_APP_ID);
  }

  private getApiKey() {
    return this.http.get(Constants.GET_YUMMLY_APP_KEY);
  }

  private init(): void {
    this.searchRecipe();
  }

  private getOptions() {
    return Rx.Observable.zip(getApiKey(), getAppId()).map((result) => {
        // Prints your keys
        console.log(`apiKey: ${result[0].text()}`);
        console.log(`appId: ${result[1].text()}`);

        // Create the RequestOptions
        let headers = new Headers();

        headers.append('Content-Type', 'application/json');
        headers.append('X-Yummly-App-Key', result[0].text());
        headers.append('X-Yummly-App-ID', result[1].text();

          const options = new RequestOptions({
            headers: headers
          });
          return options;
        });
    });

  private searchRecipe() {
    this.getOptions().map((options) => {
        // Options here is the RequestOptions object returned from the 'getOptions' function
        console.log(options);

        //Make your request to search the recipe here
      })
      .subscribe();
  }
}

new SearchRecipeDemo().init();

看看这个JSBin代码片段中模拟可观察对象的代码的一个稍微不同的版本。

谢谢您的详细回复!我了解其中的大部分内容,唯一一件我仍然不太明白的事情是,在这种情况下,我应该如何将我的appkey和appid绑定到http调用的响应。我是否在getOptions正文中进行http调用?我已使用稍微不同的代码段更新了我的答案,该代码段与您的应用程序更匹配。我用对getApiKey和getAppId函数的调用替换了模拟的观察值。注意,我还更改了这些函数,以从http.get调用返回可观察的。我真的搞不懂为什么要使用GetOptionSimple,所以我略去并重构了代码。