【问题标题】:Conditional observables with forkJoin使用 forkJoin 的条件 observables
【发布时间】:2019-02-17 05:27:40
【问题描述】:

我有一个案例,我可能需要也可能不需要将 observables 添加到列表中。然后我想forkJoin 我拥有的可观察对象,以便在所有数据可用后加载页面。

let observables: Observable<any>[] = [];

observables.push(this.taskService.getStep(this.housingTransactionId, this.task.stageReferenceId, this.task.stepReferenceId));

if (this.task.associatedChatThreadId) {
    observables.push(this.messageHubService.getChatThread(this.housingTransactionId, this.task.associatedChatThreadId));
}

if (this.task.associatedDocuments && this.task.associatedDocuments.length > 0) {
    this.task.associatedDocuments.forEach(documentId => {
        observables.push(this.documentHubService.getDocumentProperties(this.housingTransactionId, documentId));
    });
}

Observable.forkJoin(observables)
    .subscribe(([step, chatThread, ...documents]) => {

        this.step = step;           
        this.chatThread = chatThread;           
        this.documents = documents;

        this.isPageLoading = false;

    }, error => {
        this.isPageLoading = false;
        console.log(error);
    });

我遇到的问题是,如果我没有this.task.associatedChatThreadId,那么 observable 就不会添加到列表中,并且当执行 forkJoin 时,...documents 位于subscribe 方法中的chatThread 属性(嗯,第一个文档!)。

有没有办法确保来自forkJoin 的响应的定位?或者我应该/我可以使用不同的方法吗?

【问题讨论】:

    标签: javascript angular typescript rxjs observable


    【解决方案1】:

    如果不满足条件,您可以最轻松地添加带有 null 值的哑 Observable.of(null) 以保持相同的响应顺序:

    if (this.task.associatedChatThreadId) {
        observables.push(this.messageHubService....);
    } else {
        observables.push(Observable.of(null))
    }
    

    然后在订阅中你可以检查chatThread === null是否会出现在同一个位置。

    或者,您可以使用一些额外的对象将每个 Observable 包装在 observables 中,这将使其在订阅者中唯一可识别,但这会不必要地复杂,因此我个人会坚持第一个选项。

    【讨论】:

    • 干杯马丁。我最初确实做了else { observables.push(Observable.of(null)) },但感觉有点“hacky”。听起来这可能是最好的方法!
    【解决方案2】:

    另一种方法是不使用folkJoin,而是单独订阅。同时,使 isPageLoading 成为一个 BehaviorSubject,它计算您当前有多少异步请求。每次发出请求时,可以有isPageLoading.next(1),完成请求时可以有isPageLoading.next(-1)。

    【讨论】:

    • 有趣。请问可以提供代码示例吗?
    【解决方案3】:

    您可以创建一个辅助函数,该函数接受具有字符串键和可观察值的对象,并返回一个可观察对象,该可观察对象将发出具有相同键的对象,但将结果值而不是可观察值作为值。

    我真的不会说这是一个比使用 martin 建议的 of(null) 更简洁的版本,但它可能是一个替代方案。

    function namedForkJoin(map: {[key: string]: Observable<any>}): Observable<{[key: string]: any}> {
        // Get object keys
        const keys = Object.keys(map);
    
        // If our observable map is empty, we want to return an empty object
        if (keys.length === 0) {
            return of({});
        }
    
    
        // Create a fork join operation out of the available observables
        const forkJoin$ = Observable.forkJoin(...keys.map(key => map[key]))
    
        return forkJoin$
            .map(array => {
                const result = {};
                for (let index = 0; index < keys.length; index++) {
                    result[keys[index]] = array[index];
                }
    
            }));
    }
    

    请记住,我目前没有在这里运行 angular 或 rxjs,所以我无法验证该功能是否真的有效。但想法是: 1. 从输入映射中获取键。 2. 使用键获取可观察的数组并将其传递给 fork join。 3. 添加一个映射函数,将结果数组转换回对象。

    【讨论】:

    • 感谢您提出替代方案,汤姆!我确实同意了马丁使用 (null) 的答案,但这看起来是一种很酷的方式。
    • 不客气。我只是想表明有一种不同的方法可以做到这一点。但是对于您的示例,我也会使用 of(null) 的路线,只是因为这样更直接。复杂性越低通常越好;)
    猜你喜欢
    • 2017-08-27
    • 1970-01-01
    • 2021-03-10
    • 1970-01-01
    • 1970-01-01
    • 2018-03-13
    • 1970-01-01
    • 1970-01-01
    • 2020-04-19
    相关资源
    最近更新 更多