Я немного борюсь с пониманием и применением @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))),
)
)
)
Я очень доволен этим решением, однако мне не нравится предложение If ELSE в MAP как часть эффекта DOWNLOAD_EPISODE.
В идеале я хочу разделить поток там, если тип равен 3, я хочу пойти по маршруту А, который всегда отправляет episodeActions.DownloadProgressEpisodes
с полезной нагрузкой Episode.
Всякий раз, когда это тип 4, последнее отправленное событие, я хочу получить маршрут B в потоке и вызвать episodeActions.DownloadEpisodesSuccess
с телом ответа.
Я пытался добавить больше эффектов, но всегда оказывался в ситуации, когда поток результатов this.episodesService.download
будет либо обновлением прогресса, либо телом ответа. Для обновления хода выполнения я требую, чтобы запись Эпизода могла вызывать редуктор и обновлять ход Эпизода.
Я пытался использовать вещи в качестве установки filter( response => response.type === 4 )
после DownloadProgressEpisodes
, но до DownloadEpisodesSuccess
, надеясь, что это позволит итерации до того, как фильтр справится с прогрессом, а затем отфильтрует все остальное до Успешного действия.
Затем я узнал, что потоки не работают.
Я также пытался last()
, который работал, но не позволял мне отправлять действия отклика (журнал консоли работал), но только действие DownloadEpisodesSuccess после отправки last()
.
Итак, если есть возможность разделить поток и работать с event.type 3
по-другому, тогда event.type 4
меня это заинтересует.
***** UPDATE *******
Я хочу оставить исходный вопрос, однако я столкнулся с требованием регулирования, потому что я отправлял много действий для больших файлов.
Я пробовал оператор throttle
, но это подавляло бы выбросы, но могло также подавлять последний выброс с телом ответа. Я попытался разделить его на partition
, но я не думаю, что это возможно. После момента лампочки я решил создать 2 эффекта для DOWNLOAD_EPISODE
, один из которых захватывает только все event.type === 3
и регулирует его, а другой эффект использует оператор last
и работает только с event.type === 4
.
См. Код:
@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 ) ) ),
)
)
)
Как вы думаете, это лучшее решение? Тип 3 и 4 разделены, и я могу задушить его.
* Update2:
Это создает проблему, при которой Progress Action
с прогрессом 97% может быть вызвано после действия Download Success
с прогрессом 100%.
Каждый раз, когда я сталкиваюсь с новым вызовом ...
SampleTime(500)
, кажется, работает, так как он не генерирует событие типа 3 после того, как событие типа 4 вступило в силу.
* update3: Я только что узнал, что я звоню Download
дважды в результате, вздох. Я вернулся на круги своя, пытаясь задушить поток HttpEvent из episodeService.download.