Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

event和effect的使用问题 #87

Closed
ScarboroughCoral opened this issue Sep 13, 2023 · 4 comments
Closed

event和effect的使用问题 #87

ScarboroughCoral opened this issue Sep 13, 2023 · 4 comments

Comments

@ScarboroughCoral
Copy link

现在有这样一个业务方法

const downloadId = downloader.startDownload(url, {
 onProgress: () => {},
 onFinish: () => {},
 onFailed: () => {},
})
download.cancelDownload(downloadId)

使用remesh定义如下模型
downloadCommand
cancelDownloadCommand
downloadStartedEvent
progressUpdatedEvent
downloadSuccessEvent
downloadFailedEvent

按我的理解 download.startDownload发生发生在downloadStartedEvent的effect,如果下载的过程中发起了cancelDownloadCommand,此时downloadId应该在state里面了但是不清楚什么时机保存的,是应该在effect触发一个额外的事件downloadIdGotEvent吗?

@github-actions
Copy link

Thank feedback. We will check it later:-)

@Lucifier129
Copy link
Collaborator

可以将 downloader 封装成 rxjsObservable 内部管理 cancel 逻辑.

大概像下面这样处理:

import { Observable, takeUntil, switchMap } from 'rxjs'

type DownloadState = {
    type: 'progress'
    progress: number
} | {
    type: 'error'
    error: string
} | {
    type: 'success'
    file: Blob
}

const download = (url: string) => {
    return new Observable<DownloadState>(subscriber => {
        const downloadId = downloader.startDownload(url, {
            onProgress: (progress) => {
                subscriber.next({ type: 'progress', progress })
            },
            onFinish: (file: Blob) => {
                subscriber.next({ type: 'success', file })
                subscriber.complete()
            },
            onFailed: (message: string) => {
                subscriber.next({ type: 'error', error: message })
                subscriber.complete()
            },
        })

        const unsubscribe = () => {
            downloader.cancelDownload(downloadId)
        }

        return unsubscribe
    })
}


domain.effect({
    name: 'DownloadEffect',
    impl: ({ fromEvent }) => {
        return fromEvent(DownloadStartedEvent).pipe(
            switchMap(event => {
                // cancel download if user cancels it
                return download(event.url).pipe(
                    takeUntil(fromEvent(DownloadCancelledEvent)),
                )
            }),
            map(state => {
                switch (state.type) {
                    case 'progress':
                        return DownloadProgressEvent(state.progress)
                    case 'error':
                        return DownloadFailedEvent(state.error)
                    case 'success':
                        return DownloadFinishedEvent(state.file)
                }
            })
        )
    }
})

@ScarboroughCoral
Copy link
Author

可以将 downloader 封装成 rxjsObservable 内部管理 cancel 逻辑.

大概像下面这样处理:

import { Observable, takeUntil, switchMap } from 'rxjs'

type DownloadState = {
    type: 'progress'
    progress: number
} | {
    type: 'error'
    error: string
} | {
    type: 'success'
    file: Blob
}

const download = (url: string) => {
    return new Observable<DownloadState>(subscriber => {
        const downloadId = downloader.startDownload(url, {
            onProgress: (progress) => {
                subscriber.next({ type: 'progress', progress })
            },
            onFinish: (file: Blob) => {
                subscriber.next({ type: 'success', file })
                subscriber.complete()
            },
            onFailed: (message: string) => {
                subscriber.next({ type: 'error', error: message })
                subscriber.complete()
            },
        })

        const unsubscribe = () => {
            downloader.cancelDownload(downloadId)
        }

        return unsubscribe
    })
}


domain.effect({
    name: 'DownloadEffect',
    impl: ({ fromEvent }) => {
        return fromEvent(DownloadStartedEvent).pipe(
            switchMap(event => {
                // cancel download if user cancels it
                return download(event.url).pipe(
                    takeUntil(fromEvent(DownloadCancelledEvent)),
                )
            }),
            map(state => {
                switch (state.type) {
                    case 'progress':
                        return DownloadProgressEvent(state.progress)
                    case 'error':
                        return DownloadFailedEvent(state.error)
                    case 'success':
                        return DownloadFinishedEvent(state.file)
                }
            })
        )
    }
})

感谢! 太优雅了!

@Lucifier129
Copy link
Collaborator

Lucifier129 commented Sep 14, 2023

如果要支持同时下载很多个,可以将 switchMap 替换成 mergeMap,并且根据 DownloadCancelledEvent 携带的 url 进行过滤。以及拓展 DownloadState使之携带 url等数据

大概像下面这样:

domain.effect({
    name: 'DownloadEffect',
    impl: ({ fromEvent }) => {
        return fromEvent(DownloadStartedEvent).pipe(
            // for each download event, create a stream of progress events
            // merge them together instead of switching
            mergeMap(event => {
                // create a stream of cancel events for this download
                const cancel$ = fromEvent(DownloadCancelledEvent).pipe(
                    filter(cancelledEvent => cancelledEvent.url === event.url)
                )
                // cancel download if user cancels it
                return download(event.url).pipe(takeUntil(cancel$))
            }),
            map(state => {
                switch (state.type) {
                    case 'progress':
                        return DownloadProgressEvent(state)
                    case 'error':
                        return DownloadFailedEvent(state)
                    case 'success':
                        return DownloadFinishedEvent(state)
                }
            })
        )
    }
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants