【问题标题】:How to wait loading of data in TypeScript observer?如何在 TypeScript 观察者中等待数据加载?
【发布时间】:2018-04-11 12:59:50
【问题描述】:

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

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

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

public getClasses() {
    return this.classesSubjects;
  }

所以,我一直使用这个:

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

当我调用a.getClasses(); 时,它返回空对象,因为之前的方法没有得到服务器的响应。

【问题讨论】:

  • 我可以把 a.getClasses(); 放在里面:.subscribe(data => {} 但我需要把它放在外面

标签: typescript observable typescript2.0 angular2-observables


【解决方案1】:

简答:

getClassesAndSubjects 返回一个Observable。每当您需要将值存储在classesSubjects 时,请订阅getClassesAndSubjects()

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

长答案:

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

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

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

【讨论】:

  • 中肯的建议。对于 95% 的情况,由 Promises 支持的 async/await 会导致代码直接、可维护和高度可读,而 Observables 虽功能强大,但坦率地说是不必要的。在一些项目中,我添加了一个 SimpleHttpClient,它大约是 6 行代码,委托给底层 Angular HttpClient
  • 我更新了我的答案以对比async/await 方法。具有讽刺意味的是,它不仅运作良好,而且使问题本身无效。
  • 我的观察者被叫了两次
  • @dooglu 这个答案中没有代码,所以我看不出作者怎么可能负责。如果您使用我在回答中写的代码,那么我可以向您保证,它不会导致双重订阅
【解决方案2】:

您应该概括getClassesAndSubjects 调整它以返回一个值,在本例中为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 属性会更干净。此外,考虑到它返回的内容,它在其姊妹方法getClassesAndSubjects 的上下文中具有一个最令人困惑的名称。所以我只是删除了该方法。

附录:

async/await 已经被提出,并且是一种优雅的方式,可以将编写异步编程从回调的乏味和不可维护性中解脱出来,我将展示这将如何与 async/await 一起工作标准承诺。

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

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

【讨论】:

    【解决方案3】:

    getClassesAndSubjects 执行异步操作,因此,当您调用getClasses 时,操作尚未完成。你需要从getClassesAndSubjects 返回一个 observable 或一个 promise:

    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。在这种情况下,异步函数必须返回一个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 返回的promise 被履行或拒绝,因此,当a.getClasses 被执行时,数据就在那里。当然,这个函数被“挂起”并不意味着应用程序也被挂起。发生的情况是,在后台,异步函数被分解为多个部分,第二部分(在await 之后)在Promise.then 方法中执行。但是编译器会为你做这件事,所以解决方案要优雅得多。

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

    【讨论】:

    • 是的,我知道,您建议将观察者添加到getClassesAndSubjects 并订阅它?
    • 是的。事实上,是唯一的解决方案。好吧,你可以使用 async/await,但本质上完全一样,只是更优雅一点。我可以将其添加到我的答案中
    • @OscarPaz 我想喜欢这个,但anys 正在杀死我。只需关闭类型并让编译器推断它,尤其是在从上下文(问题)中不知道任何更好的东西时
    • 如果可能的话,您可以删除anys 并自己输入类型,或者让编译器推断它们。我将any 放在示例中只是因为我不知道类型,而不是因为我完全喜欢any
    • 如果它让你非常恼火,我将 any 更改为占位符 your-type :-)
    猜你喜欢
    • 1970-01-01
    • 2019-08-20
    • 2016-01-17
    • 1970-01-01
    • 1970-01-01
    • 2020-03-08
    • 2019-08-20
    • 2015-01-28
    • 1970-01-01
    相关资源
    最近更新 更多