【问题标题】:Async initialization of Angular 2 serviceAngular 2 服务的异步初始化
【发布时间】:2016-07-23 02:07:59
【问题描述】:

我有一个 Angular 2 服务,它在初始化时需要执行异步工作,并且在初始化完成之前不应该可用。

@Injectable()
export class Api {
    private user;
    private storage;

    constructor(private http: Http) {
        this.storage = LocalStorage;
        this.storage.get('user').then(json => {
            if (json !== "") {
                this.user = JSON.parse(json);
            }
        });        
    }

    // one of many methods
    public getSomethingFromServer() {
        // make a http request that depends on this.user
    }
}

就目前而言,此服务已初始化,并立即返回给使用它的任何组件。然后该组件在其ngOnInit 中调用getSomethingFromServer(),但此时Api.user 未初始化,因此发送了错误的请求。

生命周期挂钩(OnInit、OnActivate 等)不适用于服务,只能用于组件和指令,所以我不能使用它们。

从get()调用存储Promise将需要依赖于用户的所有不同方法等待它,导致大量代码重复。

在 Angular 2 中进行服务异步初始化的推荐方法是什么?

【问题讨论】:

  • 在某些情况下使用APP_INITIALIZER 可能是一种方法,例如如果您希望只调用一次 init,请在此处查看示例 stackoverflow.com/questions/49686406/… 我知道您的 q 已经回答,但这可能对将来的参考有用

标签: typescript angular angular2-services


【解决方案1】:

您可以利用 flatMap 运算符来利用 observable 来做到这一点。如果用户不在那里,您可以等待它然后链接目标请求。

这是一个示例:

@Injectable()
export class Api {
  private user;
  private storage;
  private userInitialized = new Subject();

  constructor(private http: Http) {
    this.storage = LocalStorage;
    this.storage.get('user').then(json => {
      if (json !== "") {
        this.user = JSON.parse(json);
        this.userInitialized.next(this.user);
      }
    });        
  }

  // one of many methods
  public getSomethingFromServer(): Observable<...> {
    // make a http request that depends on this.user
    if (this.user) {
      return this.http.get(...).map(...);
    } else {
      return this.userInitialized.flatMap((user) => {
        return this.http.get(...).map(...);
      });
    }
  }
}

【讨论】:

  • 我想到了这个想法。然而,我有很多这样的getSomethingFromServer,这需要他们所有人都跳这个小舞。有没有办法为此使用异步工厂模式?
  • 我想得越多,你的解决方案听起来就越好。我正在尝试实施它,但我遇到了一些麻烦。 Subject 来自哪里?是Rx.Subject吗?那似乎没有emit 方法。一般来说,我需要什么进口?
  • 是的,你是对的!有一个错字:它是next 而不是emit(emit 是在使用EventEmitter 类时)。 Subject 来自 rxjs,可以这样导入:import {Subject} from 'rxjs/Subject'; 或 import {Subject} from 'rxjs/Rx';。
【解决方案2】:

在使用了蒂埃里的答案一段时间后,我发现它只能工作一次,但它确实让我走上了正确的道路。我不得不存储用户的承诺,并创建一个新的 observable,然后是 flatMap-ed。

@Injectable()
export class Api {
  private userPromise: Promise<User>;

  constructor(private http: Http) {
    this.userPromise = LocalStorage.get('user').then(json => {
      if (json !== "") {
        return JSON.parse(json);
      }
      return null;
    });        
  }

  public getSomethingFromServer() {
      return Observable.fromPromise(this.userPromise).flatMap((user) => {
        return this.http.get(...).map(...);
      });
    }
  }
}

这可确保 flatMap 函数在每次被调用时都能获取用户,而不是像 Thierry 的回答那样只是第一次。

【讨论】:

  • 如果用户存在于 LocalStorage 中怎么办?你不是每次都从http获取吗?
  • @MadHollander OP 只是想在执行 http 调用之前等待服务的引导程序 - http 调用不会吸引用户,它只是从服务器获取需要的东西预先解决的用户
猜你喜欢
  • 2017-08-19
  • 2018-04-03
  • 2015-09-15
  • 2013-04-23
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
相关资源
最近更新 更多