Javascript 如何在RxJS observable中添加额外字段并等待其完成?

Javascript 如何在RxJS observable中添加额外字段并等待其完成?,javascript,rxjs,Javascript,Rxjs,我有一个observable,它返回项,我只需要一个特定项,我想从一个http请求中添加一些额外的字段,该请求也是可观察的。下面的代码是我如何试图实现这一点,但它不起作用。我需要完成所有观察,以获取ngOnInit中的完整项目数据。我错过了什么 ngOnInit(){ MyItemsBServable$(this.store,items,items.data) .map(items=>items.find( 项目=>{ return item.id==id } )) .concatMap(项目=

我有一个observable,它返回
,我只需要一个特定项,我想从一个http请求中添加一些额外的字段,该请求也是可观察的。下面的代码是我如何试图实现这一点,但它不起作用。我需要完成所有观察,以获取
ngOnInit
中的完整项目数据。我错过了什么

ngOnInit(){
MyItemsBServable$(this.store,items,items.data)
.map(items=>items.find(
项目=>{
return item.id==id
}
))
.concatMap(项目=>{
返回此.apiService.get(`/items/${item.id}/extradata`).map(extra=>({
…项目,
额外的
}))
})
.订阅(项目=>{
//我希望项目在这里有额外的字段。
this.item=项目
})
//此处的此项应已完成。
}

您使用的是angular和rxjs的旧版本吗?以目前的方式,map不是一种可观测的方法

相反,我认为你会寻找这样的东西

function ngOnInit() {
  myItemsObservable$(this.store, items, items.data)
    .pipe(
      filter(item => item.id === id), // assuming your observable emits each item separately. Otherwise, use your map statement above.
      take(1), // do you need this? I'm guessing you wouldn't.
      concatMap(item => this.apiService.get(`,/items/${item.id}/extradata`)
        .pipe(
          map(extra => Object.assign({}, item, {extra})) // if I understand what you are wanting here, otherwise replace "{extra}" with "extra", or go back to your original notation
        )
      )
    )
    .subscribe(
      item => {
          // I expect item to have extra fields here.
          this.item = item
      }
    )
  // this.item here should already be complete.
}

让我们看一下代码的简化版本:

1函数ngOnInit(){
2 MyItemsBServable$().subscribe(item=>this.item=item);
3 console.log(this.item);//未定义
4  }
实际上,您调用的是两个函数,它们一个接一个地立即执行

第2行创建了一个subscription对象,该对象在observable中启动数据流。但是执行不会在第2行之后暂停。因此,在observable中的异步任务完成之前,执行第3行。这就是为什么第3行的
项仍然
未定义的原因

希望您能了解您的评论不正确的原因:

// this.item here should already be complete.
您正在将一个函数(
item=>this.item=item
)传递给
subscribe()
方法,该方法在可观测到的排放发生时处理这些排放。这是代码中实际具有发出值的位置

因此,如果我们将
console.log()
移动到订阅中,
此.item
将不再是未定义的:

1函数ngOnInit(){
2 MyItemsBServable$()。订阅(项目=>{
3.本项目=项目;
4 console.log(this.item);//未定义:-)
5    });
6  }
回答问题的两个部分:

如何在RxJS observable中添加额外字段

你已经在这样做了。您已经使用了
map
concatMap
操作符将源可观察到的值修改为所需的值

…然后等待它完成

好吧,你不会“等待”它本身。使用RxJS,您可以定义数据流的行为。您可以访问实际数据的唯一位置是subscribe内部

但是。。。您可以直接在代码的其他部分引用可观测值,而不是订阅,然后将数据从可观测值复制到另一个变量

让我们将您的代码分成几个不同的部分,以便更容易了解如何在不订阅的情况下引用不同的可观察源:

id$=this.route.paramMap.pipe(//这可能来自表单控件输入
params=>params.get('id')//或其他一些可观察的源。
);
allItems$=MyItemsBServable$(this.store,items,items.data);
getItem$(id){
返回此.allItems$.pipe(
映射(items=>items.find(i.id==id))
);
}
getExtraData$(id){
返回此.apiService.get(`/items/${id}/extradata`);
}
项$=此.id$.pipe(
开关映射(id=>getItem$(id)),
switchMap(item=>this.getExtraData(item.id).pipe(
映射(extra=>({…项,…extra}))
))
);
}
请参见
项$
的定义如何从
id$
开始?这意味着每当
id$
发出一个新值时,
item$
将自动调用
getItem$()
,然后
getExtraData()
发出这个新项。我们不需要订阅就可以做到这一点

我们可以简单地定义一个可观察对象,从另一个可观察对象开始,然后
.pipe()
对排放进行转换以满足我们的需要

我们基本上设计了一个observable,它可以在商店中的物品发生变化时,或者在我们选择的
id$
发出新值时发出。从某种意义上说,我们已经建立了
项目$
来表示我们的
项目
,它将始终是最新的,包括附加其“额外数据”。这是非常强大的。现在我们可以用它了

请注意,
item$
的定义不需要在
ngOnInit
中;它实际上可以直接在您的组件上运行

我们确实可以订阅我们的组件。。。但我们通常可以在模板中使用:

<div *ngIf="item$ | async as item">
  <h1>{{ item.name }}</h1>
  <ul>
    <li>{{ item.description  }}</li>
    <li>{{ item.someProperty }}</li>
  </ul>
</div>

{{item.name}
  • {{item.description}}
  • {{item.someProperty}
如果您发现自己经常订阅组件,只是为了将数据复制到局部变量,只是为了让模板使用;我鼓励你停下来问问自己这真的很有必要。大多数情况下,您可以定义一个可观察对象,它可以准确地发出视图所需的数据,而无需订阅


RxJS提供了许多运算符和静态函数,可以轻松创建具有各种常见行为的可观察对象。

可观察代码后的此项仍然未定义,因此它仍然不会等待可观察代码完成。这是一个很好的答案。我试图用async做一些类似的事情,但感到困惑,因为这个.item在init中是可用的,而我没有添加额外的请求。非常感谢。