我在理解和应用@Effects作为我的剧集“下载”选项时有些挣扎。我在另一个问题上得到了一些帮助,这是我最近的调酒。
快速概述:用户单击下载可调度第一个效果中捕获的DOWNLOAD_EPISODE
操作。下载调用返回HttpEvents流和最终的HttpResponse。
在event.type === 3
期间,我要报告下载进度。当event.type === 4
到达时,我可以称呼成功,例如可以创建一个Blob。
服务episodesService
:
download( episode: Episode ): Observable<HttpEvent<any>> | Observable<HttpResponse<any>> {
// const url = encodeURIComponent( episode.url.url );
const url = 'https%3A%2F%2Fwww.sample-videos.com%2Faudio%2Fmp3%2Fcrowd-cheering.mp3';
const req = new HttpRequest( 'GET', 'http://localhost:3000/episodes/' + url, {
reportProgress: true,
responseType: 'blob'
} );
return this.http.request( req );
}
downloadSuccess( response: any ): Observable<any> {
console.log( 'calling download success', response );
if ( response.body ) {
var blob = new Blob( [ response.body ], { type: response.body.type } );
console.log( 'blob', blob );
}
return of( { status: 'done' } );
}
getHttpProgress( event: HttpEvent<any> | HttpResponse<Blob> ): Observable<DownloadProgress> {
switch ( event.type ) {
case HttpEventType.DownloadProgress:
const progress = Math.round( 100 * event.loaded / event.total );
return of( { ...event, progress } );
case HttpEventType.Response:
const { body, type } = event;
return of( { body, type, progress: 100 } );
default:
return of( { ...event, progress: 0 } );
}
}
效果:
@Effect()
downloadEpisode$ = this.actions$.pipe(
ofType<episodeActions.DownloadEpisodes>( episodeActions.DOWNLOAD_EPISODE ),
switchMap( ( { payload } ) => this.episodesService.download( payload )
.pipe(
switchMap( (response: HttpEvent<any> | HttpResponse<any>) => this.episodesService.getHttpProgress( response ) ), //merge in the progress
map( ( response: fromServices.DownloadProgress ) => {
// update the progress in the episode
//
if ( response.type <= 3 ) {
return new episodeActions.DownloadProgressEpisodes( { ...payload, download: {
progress: response.progress
} } );
// pass the Blob on the download response
//
} else if ( response.type === 4 ){
return new episodeActions.DownloadEpisodesSuccess( response );
}
} ),
catchError( error => of( new episodeActions.DownloadEpisodesFail( error ) ) ),
)
)
)
@Effect( { dispatch: false } )
processDownloadEpisodeSuccess$ = this.actions$.pipe(
ofType<any>( episodeActions.DOWNLOAD_EPISODE_SUCCESS ),
switchMap( ( { payload } ) => this.episodesService
.downloadSuccess( payload ).pipe(
tap( response => console.log( 'response', payload,response ) ),
// catchError(err => of(new episodeActions.ProcessEpisodesFail(error))),
)
)
)
我对此解决方案感到非常满意,但是我不喜欢MAP中的If ELSE子句作为DOWNLOAD_EPISODE效果的一部分。
理想情况下,我想在那儿拆分流,如果类型为3,我想走路线A,该路线始终以Episode有效载荷调度episodeActions.DownloadProgressEpisodes
。
每当它是类型4(最后一个发出的事件)时,我都希望在流中获得B路由,并使用响应主体调用episodeActions.DownloadEpisodesSuccess
。
我试图添加更多效果,但是我总是遇到这样的情况,this.episodesService.download
的结果流要么是进度更新,要么是响应正文。对于进度更新,我要求Episode记录能够调用reducer并更新Episode的进度。
我尝试使用在filter( response => response.type === 4 )
之后但在DownloadProgressEpisodes
之前设置DownloadEpisodesSuccess
的情况,希望它允许在过滤器处理进度之前进行迭代,然后过滤其余部分成功行动。
然后,我了解到这不是流的工作原理。
我还尝试了last()
确实有效,但是它不允许我分派响应操作(控制台日志确实有效),但是仅分派last()
之后的DownloadEpisodesSuccess操作。 / p>
因此,如果可以拆分流并以与event.type 3
不同的方式处理event.type 4
,则对此我会感兴趣。
*****更新*******
我想保留最初的问题,但是我确实遇到了节流的要求,因为我为大型文件调度了很多操作。
我尝试了throttle
运算符,但它可以抑制发射,但也可以抑制响应主体的最后一个发射。我尝试使用partition
进行拆分,但我认为这是不可能的。经过一个灯泡瞬间,我决定为DOWNLOAD_EPISODE
创建2个效果,一个仅捕获所有event.type === 3
并对其进行调节,而另一个效果则使用last
运算符并且仅处理{{1 }}。
查看代码:
event.type === 4
您认为这是最好的解决方案吗?类型3和4被拆分了,我可以限制它。
* update2:
确实会产生一个问题,其中进度为100%的 @Effect( )
downloadEpisode$ = this.actions$.pipe(
ofType<episodeActions.DownloadEpisodes>( episodeActions.DOWNLOAD_EPISODE ),
switchMap( ( { payload } ) => this.episodesService.download( payload )
.pipe(
switchMap( (response: HttpEvent<any> | HttpResponse<any>) => this.episodesService.getHttpProgress( response ) ),
throttleTime( 500 ),
map( ( response: fromServices.DownloadProgress ) => {
console.log('Type 3', response);
// update the progress in the episode
if ( response.type <= 3) {
return new episodeActions.DownloadProgressEpisodes( { ...payload, download: {
progress: response.progress
} } );
}
} ),
catchError( error => of( new episodeActions.DownloadEpisodesFail( error ) ) ),
)
)
)
@Effect( )
downloadEpisodeLast$ = this.actions$.pipe(
ofType<episodeActions.DownloadEpisodes>( episodeActions.DOWNLOAD_EPISODE ),
switchMap( ( { payload } ) => this.episodesService.download( payload )
.pipe(
switchMap( (response: HttpEvent<any> | HttpResponse<any>) => this.episodesService.getHttpProgress( response ) ),
last(),
map( ( response: fromServices.DownloadProgress ) => {
console.log('Type 4', response);
if ( response.type === 4 ){
return new episodeActions.DownloadEpisodesSuccess( response, { ...payload, download: {
progress: response.progress
} } );
}
} ),
catchError( error => of( new episodeActions.DownloadEpisodesFail( error ) ) ),
)
)
)
操作之后可以触发进度为97%的Progress Action
。
每当我遇到新的挑战...
Download Success
似乎可以正常工作,因为Type 4事件进入后似乎并没有引发Type 3事件。
* update3:实际上,我发现我现在两次致电SampleTime(500)
,叹了口气。我回到第一个方格,试图限制来自EpisodeService.download的HttpEvent流。
答案 0 :(得分:1)
我认为,如果您不想使用if else
语句,则必须创建不同的效果。
在当前操作中,您将分派新操作,例如DownloadEpisodeProgess
,其类型在有效载荷中。
然后,您将创建两个监听此动作的效果,并通过类型对它们进行相应的过滤,一个效果将用于调度DownloadProgressEpisodes
,另一个效果用于调度DownloadEpisodesSuccess
。
另一种可能的解决方案是partition operator,但我没有将其与NgRx效果结合使用。
请记住,对于您进行的每个订阅都会有性能成本,我个人并不在乎if else
语句。