如何在 TypeScript 观察器中等待加载数据?

How to wait loading of data in TypeScript observer?

我在 class 中有方法从服务器加载检索信息:

public getClassesAndSubjects(school: number, whenDate: string) {
    this.classService.GetClassesAndSubjects(school, whenDate).subscribe(data => {
      if (!data.hasOwnProperty('errors')) {
        this.classesSubjects = data;

      }
    }, error => {
      console.log("ERROR loading GetClassesAndSubjects: " + error);
    });
  }

在成功的结果中它填充对象:this.class Subjects

还有另一种方法returns这个数据:

public getClasses() {
    return this.classesSubjects;
  }

所以,我一直使用这个:

let a = new ClassObj();
a.getClassesAndSubjects(1,'2018-01-01');
a.getClasses();

当我调用 a.getClasses(); 时,它 returns 空对象,因为之前的方法未从服务器提供响应。

getClassesAndSubjects 执行异步操作,因此,当您调用 getClasses 时操作尚未完成。您需要 return 来自 getClassesAndSubjects 的可观察对象或承诺:

public getClassesAndSubjects(school: number, whenData: string): Observable<your-type> {
    const observable = this.classService.GetClassesAndSubjects(school, whenDate);
    observable.subscribe(data => {
      if (!data.hasOwnProperty('errors')) {
        this.classesSubjects = data;

      }
    }, error => {
      console.log("ERROR loading GetClassesAndSubjects: " + error);
    });

    return observable;
}

现在:

a.getClassesAndSubjects(1,'2018-01-01').subscribe(value => {
     a.getClasses();
});

如果函数执行异步操作,任何依赖于此类操作结果的操作都必须考虑到这一点,并等待操作完成。

您也可以使用 async/await。在这种情况下,异步函数必须 return a Promise:

public async getClassesAndSubjects(school: number, whenData: string): Promise<your-type> {
    const observable = this.classService.GetClassesAndSubjects(school, whenDate);
    observable.subscribe(data => {
      if (!data.hasOwnProperty('errors')) {
        this.classesSubjects = data;

      }
    }, error => {
      console.log("ERROR loading GetClassesAndSubjects: " + error);
    });

    return observable.toPromise();
}

现在,无论您想在哪里使用它:

async function whatever() {
    // ...
    await a.getClassesAndSubjects(1, '2018-01-01');
    a.getClasses();
}

通过这样做,函数 whatever 的执行将被暂停,直到由 a.getClassesAndSubjects 做出的承诺 return 被履行或拒绝,因此,当 a.getClasses 被执行时,数据在那里。当然,这个函数得到 'suspended' 并不意味着应用程序也被挂起。发生的事情是,在幕后,异步函数被分解成多个部分,第二部分(在 await 之后)在 Promise.then 方法中执行。但是编译器会为你做这些,所以解决方案要优雅得多。

您需要记住,任何使用 await 的函数都必须声明为 async

您应该概括 getClassesAndSubjects 将其调整为 return 一个值,在本例中为 Observable。它现在可以组合,而不是调用 subscribe

getClassesAndSubjects(school: number, whenDate: string) {
  return this.classService.getClassesAndSubjects(school, whenDate)
    .map(data => {
      if ('errors' in data) { // type guard
        throw Error(data.errors);
      }
      return data;
    });
}

const c = new ClassObj();

c.getClassesAndSubjects(1,'2018-01-01').subscribe(classesAndSubjects => {
  this.classesAndSubjects = classesAndSubjects;
}, handleError);


// outside of the class!
export function handleError(error: any) {
  console.log("ERROR loading GetClassesAndSubjects: " + error);
}

请注意,getClasses 是一种毫无意义的方法,它可能不存在,如果存在,get 属性 会更干净。此外,鉴于其 return 的含义,它在其姊妹方法 getClassesAndSubjects 的上下文中有一个最令人困惑的名称。所以我只是删除了该方法。

附录:

async/await 已经提出并作为一种优雅的方式将编写异步编程从回调的单调乏味和不可维护性中解放出来,我将展示它如何与 async/await 超出标准 Promise。

import 'rxjs/add/operator/toPromise';
// ...

async getClassesAndSubjects(school: number, whenDate: string) {
  const data = await this.classService.getClassesAndSubjects(school, whenDate)
    .toPromise();
  if ('errors' in data) { // type guard
    throw Error(data.errors);
  }
  return data;
}

// ...

const c = new ClassObj();

try {
  this.classesAndSubjects = await this.getClassesAndSubjects(1,'2018-01-01');
}
catch (e) {
  handleError(e);
}

优势很明显,无需任何调整即可使用标准句法结构,保持线性控制流的清晰度,最重要的是一直处理常规值和异常。这种方法不需要 subscribe vs return 困境,因为一切都是组合的。通过这种方式,我们回避了问题的全部原因。当然它没有那么强大,但它也更简单。

简答:

getClassesAndSubjectsreturn一个Observable。每当您需要存储在 classesSubjects 中的值时,请订阅 getClassesAndSubjects().

然而,虽然它在语法上可行,但它可能不是最佳选择。最好的选择,取决于。 :-)

长答案:

问题是异步代码执行造成的。

在JavaScript/TypeScript中,有几种方法可以处理这些事情。选择哪个选项,取决于您的需要:

我强烈建议在修复代码之前阅读(并尝试)有关这些主题的教程。预先投入时间来了解这些先决条件概念将为您在将来节省大量错误 fixing/debugging 时间。