【问题标题】:Wait for a Subscription and a Promise in Angular 4在 Angular 4 中等待订阅和承诺
【发布时间】:2017-08-23 20:18:45
【问题描述】:

我是 Angular 4 的新手,所以我接受改变我的方法可能会更好。

我拥有的是这样的:

ngOnInit() {
    this.route.paramMap
        .switchMap((params: ParamMap) => {
            var id = +params.get('id');
            return this.service.getOne(id);
        }).subscribe(data => {
            if (data.clientID === 0) {
                data.clientID = +this.route.snapshot.queryParams["parentId"];
            }
            this.model = data;
        },); // Subscription

    this.lookupService.getList("clients").then(data => {
        this.clients = data;
    }); // Promise
}

在客户端,我需要进行另一个服务器调用,但前提是我有来自上述两个调用的数据。换句话说,我需要 Subscription 来完成(好吧,不是完成,而是设置值)和 Promise 来完成。

是否有类似Promise.all 的东西也可以确保this.model 已在Subscription 调用中设置?

编辑: 我可以串联运行它们,但出于性能原因,我想并行运行它们。

编辑 2: Promise 旨在加载查找数据以仅填充一次下拉列表。 Subscription 旨在在每次更改 URL 中的“id”时更改主模型。

【问题讨论】:

  • 所以lookupService.getList() 应该只运行一次。 service.getOne() 应该在每次新参数到达时运行,对吗?好的,第三个电话呢?你说要等另外两个生产,但是第一个生产之后呢?第三次调用应该只进行一次还是每次新参数到达时进行?
  • @BeetleJuice 我已经将第三个调用简化为只运行一个本地过滤器函数,但它需要在service.getOne() 更新后与每个新参数一起运行。
  • 我重写了我的答案并提供了一个演示。如果您找到更好的解决方案,请教给我们!

标签: angular typescript promise rxjs


【解决方案1】:
  1. 在 promise 和模型获取 observable 上使用 .zip()。它将输出一个结果数组,您可以使用这些结果来填充本地类属性。此时您可以调用第三个函数(本地过滤器)。一旦 Promise 完成,.zip 也会完成,所以这条链只有一个输出。

  2. 一旦.zip() 完成,监听新的路由参数并获取相应的模型。当它们到达时,设置您的本地类属性并运行您的本地过滤器

  3. 订阅触发全链。

    ngOnInit(){
        // Will run getOne() with every new parameter
        let getModelForParam = this.route.paramMap.switchMap(
            params => this.service.getOne(+params.get('id'))
        ).share();
    
        // Zip will wait for both below to produce, then output an array of results
        // and complete (since getList() promise completes after one call)
        Observable.zip(
            this.lookupService.getList('clients'),
            getModelForParam
        ).do(results => { // capture results in order of .zip() arguments
            this.clients = results[0];
            this.model = results[1];
            this.localFilter();// call the filter
        }).concat( // this part executes once .zip() completes
            getModelForParam // continue fetching models for new params
        ).subscribe(model => {
            this.model = model;
            this.localFilter();
        })
    }
    

Live demo

【讨论】:

  • 我相信switchMap 没有完成,因为该组件可以重复使用,并且您不能使用take(1),因为如果该组件被重复使用,您将丢失对参数的所有后续更新
  • @MaximKoretskyi 有趣的问题。我觉得你是对的。由于这可能意味着每次发出新参数时都会运行,因此我们不应该在 1 个结果后停止。更好的方法是使用zip。它还将并行进行调用,但仅在两个响应都到达时才发出。我会更新我的答案。
  • @MaximKoretskyi 实际上现在我想起来了,OP 的结果之一是承诺解决方案,这意味着一劳永逸。因此,如果要求在前两个结果到达之后进行第三次调用,则第三次调用只会发生一次(因为承诺只解析一次)。因此在实践中我看不出forkJoin / take(1)zip 之间的区别
  • 谢谢@BeetleJuice。不过,我实际上确实需要 paramMap 上的未来通知。但我不需要每次都重新加载this.lookupService.getList("clients")。另外,我尝试了您的代码示例,this.route.paramMap.switchMap('...') 的省略号中的代码永远不会运行。我将这两个添加到文件顶部:import { Observable } from "rxjs/Observable"; import 'rxjs/add/operator/zip';
  • @KevinWelsh,所以你想在每次参数更改时只运行一次this.lookupService.getListthis.service.getOne(id),然后在每次参数更改时连接结果并发出新项目?
【解决方案2】:

可以使用Observable.forkJoin,相当于Promise.all

Observable.forkJoin(this.route.paramMap
        .switchMap((params: ParamMap) => {
            var id = +params.get('id');
            return this.service.getOne(id);
        }), Observable.fromPromise(this.lookupService.getList("clients")))
        .subscribe((arrayOfResults) => {
             [firstCallResult, secondCallResult] = arrayOfResults;
             this.model = firstCallResult;
        });

编辑:

为了完成更新参数的部分,我们可以使用Observable.combineLatest操作符:

About Observable.combineLatest

由于它会在源 observable 之一发出时发出,因此订阅块将被执行。由于另一个 observable 来自一个 promise,它将完成并且不会发出更多值,因此不会再次调用 this.lookupService.getList()。

Observable.combineLatest(this.route.paramMap
            .switchMap((params: ParamMap) => {
                var id = +params.get('id');
                return this.service.getOne(id);
            }), Observable.fromPromise(this.lookupService.getList("clients")))
            .subscribe((arrayOfResults) => {
                 [firstCallResult, secondCallResult] = arrayOfResults;
                 this.model = firstCallResult;
                 // do whatever else is needed
            });

【讨论】:

  • 我们的“服务层”有几十个返回 Promise 的对象(就像 angular.io 教程中一样),并且改变会产生全球影响。但是,如果这是唯一的方法,那么我想我需要这样做。
  • @KevinWelsh 看到我的编辑。您可以使用 Observable.fromPromise() 将 promise 转换为 observable。
  • 不错。 combineLatest() 是我认为的正确运营商
  • @Matsura, combineLatest() 效果很好。谢谢。
【解决方案3】:

你可以这样做

ngOnInit() {
  this.lookupService.getList("clients").then(data => {
    this.clients = data;
    this.route.paramMap
    .switchMap((params: ParamMap) => {
        var id = +params.get('id');
        return this.service.getOne(id);
    }).subscribe(data => {
        if (data.clientID === 0) {
            data.clientID = +this.route.snapshot.queryParams["parentId"];
        }
        this.model = data;
==>Call ur method here!!!
    },); /

}); // Promise

【讨论】:

  • 我会更新问题,但我不想连续运行这些服务器调用。
猜你喜欢
  • 2018-10-04
  • 1970-01-01
  • 2018-01-17
  • 1970-01-01
  • 2019-08-31
  • 2019-03-24
  • 1970-01-01
  • 2018-02-25
  • 2017-04-04
相关资源
最近更新 更多