Angular 4 和 RxJS 5:Observable.concat() 行为异常
Angular 4 and RxJS 5: Observable.concat() behaving unexpectedly
我的 Angular 4 / TypeScript 2.3 服务有一个函数 build()
如果 class 属性 没有初始化就会出错。我正在尝试构建一个更安全的版本 -- safeBuild()
-- 它将 return 一个 Observable,它将在尝试调用 [=14= 之前等待并监听 属性 被初始化]
export class BuildService {
renderer:Renderer2; // must be set for build() below to work
// emits the new Renderer2 when renderer is set
private rendererSet$:BehaviorSubject<Renderer2> = new BehaviorSubject(null);
/** Set renderer, and notify any listener */
setRenderer(renderer:Renderer2){
this.renderer = renderer;
this.rendererSet$.next(renderer);
}
/** Returns a new DOM element. Requires renderer to be set */
build(elemTag:string){
// if renderer is not set, we can't proceed
// why is this error thrown when safeBuild() is called?
if (!this.renderer)
throw new Error('Renderer must be set before build() is run');
return this.renderer.createElement(elemTag);
}
/**
* A safe version of build(). Will wait until renderer is set
* before attempting to call build (Asynchronous)
*/
safeBuild(elemTag:string):Observable<any> {
// inform user that renderer should be set
// this warning is printed to the console as expected
if (!this.renderer)
console.warn('The build will be delayed until setRenderer() is called');
// Listen to rendererSet$, filter out the null output, and call build()
// only once the renderer is set. Why does the error still get thrown?
return Observable.concat(
this.rendererSet$.filter(e=>!!e).take(1),
Observable.of(this.build(elemTag))
)
}
}
我尝试这样构建(从另一个服务):
this.buildService.safeBuild(elemTag).subscribe(...)
在控制台中我看到:
Warn: The build will be delayed until setRenderer() is called
Error: Renderer must be set before build() is run
我预料到了警告,但直到我的应用程序的另一部分调用 setRenderer()
之后才发生任何事情。那时,subscribe()
中的代码将 运行.
为什么我会看到错误?
这是因为,你创建了一个任意 this.build
函数的 Observable returns。因为,您还没有设置渲染器,下面的行会抛出一个错误。确保你
先调用 setRenderer
函数
if (!this.renderer)
throw new Error('Renderer must be set before build() is run');
你应该能够通过如下返回一个 Observable 来解决这个问题
return Observable.concat(
this.rendererSet$.filter(e=>!!e).take(1),
this.rendererSet$.asObservable().map(() => this.build(elemTag)) // this line will execute when there is a new value set to rendererSet
)
问题是 this.build(elemTag)
在组成 concat
可观察对象时被调用 - 而不是在执行串联时被调用。
您可以使用 defer
:
解决问题
import 'rxjs/add/observable/defer';
...
return Observable.concat(
this.rendererSet$.filter(e => !!e).take(1),
Observable.defer(() => Observable.of(this.build(elemTag)))
);
或者,正如评论中指出的那样,使用 map
:
return this.rendererSet$
.filter(e => !!e)
.take(1)
.map(() => this.build(elemTag));
我的 Angular 4 / TypeScript 2.3 服务有一个函数 build()
如果 class 属性 没有初始化就会出错。我正在尝试构建一个更安全的版本 -- safeBuild()
-- 它将 return 一个 Observable,它将在尝试调用 [=14= 之前等待并监听 属性 被初始化]
export class BuildService {
renderer:Renderer2; // must be set for build() below to work
// emits the new Renderer2 when renderer is set
private rendererSet$:BehaviorSubject<Renderer2> = new BehaviorSubject(null);
/** Set renderer, and notify any listener */
setRenderer(renderer:Renderer2){
this.renderer = renderer;
this.rendererSet$.next(renderer);
}
/** Returns a new DOM element. Requires renderer to be set */
build(elemTag:string){
// if renderer is not set, we can't proceed
// why is this error thrown when safeBuild() is called?
if (!this.renderer)
throw new Error('Renderer must be set before build() is run');
return this.renderer.createElement(elemTag);
}
/**
* A safe version of build(). Will wait until renderer is set
* before attempting to call build (Asynchronous)
*/
safeBuild(elemTag:string):Observable<any> {
// inform user that renderer should be set
// this warning is printed to the console as expected
if (!this.renderer)
console.warn('The build will be delayed until setRenderer() is called');
// Listen to rendererSet$, filter out the null output, and call build()
// only once the renderer is set. Why does the error still get thrown?
return Observable.concat(
this.rendererSet$.filter(e=>!!e).take(1),
Observable.of(this.build(elemTag))
)
}
}
我尝试这样构建(从另一个服务):
this.buildService.safeBuild(elemTag).subscribe(...)
在控制台中我看到:
Warn: The build will be delayed until setRenderer() is called
Error: Renderer must be set before build() is run
我预料到了警告,但直到我的应用程序的另一部分调用 setRenderer()
之后才发生任何事情。那时,subscribe()
中的代码将 运行.
为什么我会看到错误?
这是因为,你创建了一个任意 this.build
函数的 Observable returns。因为,您还没有设置渲染器,下面的行会抛出一个错误。确保你
先调用 setRenderer
函数
if (!this.renderer)
throw new Error('Renderer must be set before build() is run');
你应该能够通过如下返回一个 Observable 来解决这个问题
return Observable.concat(
this.rendererSet$.filter(e=>!!e).take(1),
this.rendererSet$.asObservable().map(() => this.build(elemTag)) // this line will execute when there is a new value set to rendererSet
)
问题是 this.build(elemTag)
在组成 concat
可观察对象时被调用 - 而不是在执行串联时被调用。
您可以使用 defer
:
import 'rxjs/add/observable/defer';
...
return Observable.concat(
this.rendererSet$.filter(e => !!e).take(1),
Observable.defer(() => Observable.of(this.build(elemTag)))
);
或者,正如评论中指出的那样,使用 map
:
return this.rendererSet$
.filter(e => !!e)
.take(1)
.map(() => this.build(elemTag));