将mergeMap()与主题一起使用会忽略我正在合并的observable上的takeWhile()

时间:2016-10-17 17:26:14

标签: javascript typescript rxjs

我目前正试图想出一种暂停观察的方法。在github https://github.com/ReactiveX/rxjs/issues/1542看到blesh的帖子后,我想我正走在正确的轨道上。但是由于某些原因,一旦我从我的暂停主题执行switchMap(),我的观察者上的takeWhile()就会被忽略。

这正确结束:

export class CompositionService {
    cursor = -1;
    pauser = new Subject();
    interval;
    init = (slides) => {
        let waitUntil = 0;
        return this.interval = Observable
            .range(0, slides.length)
            .mergeMap((i) => {
                let next = Observable.of(i).delay(waitUntil);
                waitUntil += !!slides[i]["duration"] ? slides[i]["duration"] : 0;
                return next;
            })
            .scan((cursor) => {
                return this.cursor = cursor = slides[cursor + 1] ? cursor + 1 : -1;
            }, this.cursor)
            .map(cursor => slides[cursor])
            .takeWhile((slide) => {
                return !!slide;
            });

    };
    // these methods are not called for this sample
    play = () => {
        this.pauser.next(false);
    };
    pause = () => {
        this.pauser.next(true);
    };
};

以这种方式调用时可以正常工作:

it("should subscribe to init", (done) => {
    slides.forEach((slide, i) => {
        if (slide.duration) {
            slide.duration = slide.duration / 100;
        }
    });
    composition.init(slides).subscribe(
        (slide) => {
            console.log(slide);
        },
        (err) => {
            console.log("Error: " + err);
        },
        () => {
            done();
        });
});

虽然上一个示例的工作方式与广告一样,但当我添加一些“魔术”时,Observer永远不会结束:

export class CompositionService2 {
    cursor = -1;
    pauser = new Subject();
    interval;
    init = (slides) => {
        let waitUntil = 0;
        this.interval = Observable
            .range(0, slides.length)
            .mergeMap((i) => {
                let next = Observable.of(i).delay(waitUntil);
                waitUntil += !!slides[i]["duration"] ? slides[i]["duration"] : 0;
                return next;
            })
            .scan((cursor) => {
                return this.cursor = cursor = slides[cursor + 1] ? cursor + 1 : -1;
            }, this.cursor)
            .map(cursor => slides[cursor])
            .takeWhile((slide) => {
                return !!slide;
            });
        return this.pauser
            // leaving commented for clarity of the end game
            // .switchMap( paused => paused ? Observable.never() : this.interval );
            // however, not even a straight forward switchMap is yeilding the expected results
            .switchMap( paused => this.interval );            
    };
    play = () => {
        this.pauser.next(false);
    };
    pause = () => {
        this.pauser.next(true);
    };
};

以这种方式打电话:

it("should subscribe to init", (done) => {
    slides.forEach((slide, i) => {
        if (slide.duration) {
            slide.duration = slide.duration / 100;
        }
    });
    composition.init(slides).subscribe(
        (slide) => {
            console.log(slide);
        },
        (err) => {
            console.log("Error: " + err);
        },
        () => {
            //I never get here!!!!!
            done();
        });
    // kickstart my heart!
    composition.play();
});

任何人都有任何想法我在这里做错了吗?

1 个答案:

答案 0 :(得分:1)

您没有完成外部流。在takeWhile完成流的第一个版本中,您将完成。但是,一旦你将其嵌套在switchMap内。您只是在完成内部流,因为外部流(a Subject)永远不会完成。当它被展平时,它对用户来说似乎是一个永无止境的流。

如果要完成它,则需要在某个时刻终止流,例如:

composition.init(slides)
  .take(3)
  .subscribe(
    (slide) => {
        console.log(slide);
    },
    (err) => {
        console.log("Error: " + err);
    },
    () => {
        //I never get here!!!!!
        done();
    });

我不是非常相信Rx实际上是正确的工具,因为流不是真正设计为“暂停”,因为你实际上无法阻止Observable继续传播。您可能已经注意到在暂停之间跳转到存储状态的箍数,因此考虑使用生成器或其他库(如IxJS)可能是有意义的。但我离题了。