Rx.Observable.prototype.fromEvent()的奇怪行为

时间:2017-05-25 03:11:10

标签: javascript jquery rxjs flatmap concatmap

今天我在使用RxJS时遇到了一个奇怪的问题。请帮我检查一下。

我正在处理的问题是:“给定一系列图像网址,加载并将所有图像附加到div。”

所有要演示的代码片段都在这里:

https://pastebin.com/F3ZkH3F8

一开始,我使用the first snippet

但是,Rx.Observable.prototype.flatMap有时会使图像的顺序错误(文档中会注意到这种行为)。所以,我改为使用concatMapsecond snippet)。

这次只加载第一张图片。我花了一些时间检查问题。我怀疑事件load未来自image。但最令人困惑的情况是,当我添加一些代码来监听image的{​​{1}}事件时,它会向我显示事件被正确触发......(load)。

然后我决定使用third snippet$.Deferred)编写另一个版本。

有效......

你能告诉我这是什么问题吗?非常感谢你!

1 个答案:

答案 0 :(得分:1)

因为第一个子观察者的fromEvent(image, 'load')没有完成,所以其他子观察者正在等待。所以你应该在第一次事件后完成子观察。

使用take(1)

摘自第二段摘录

...
var loadedImageStream = Rx.Observable
                            .fromEvent(image, 'load')
                            .map(function() {                                  
                                return image;
                             })
...

添加take(1)以完成子可观察

...
var loadedImageStream = Rx.Observable
                            .fromEvent(image, 'load')
                            .map(function() {                                  
                                return image;
                             })
                            .take(1)
...

修改

使用concatMap会使图片顺序加载,因此速度很慢。

如果您通过index,则可以使用replace代替append来保留订单。在这种情况下,您可以使用flatMap,这可以实现快速并发加载。

$(function () {
    var imageURLList = [
        'https://placehold.it/500x100',
        'https://placehold.it/500x200',
        'https://placehold.it/500x300',
        'https://placehold.it/500x400',
        'https://placehold.it/500x500'
    ];
    var imagesDOM = $('#images');

    Rx.Observable
        .fromArray(imageURLList)
        .do(function (imageURL) {
            imagesDOM.append(new Image()); // placeholder
        })
        .flatMap(function (imageURL, index) {
            var image = new Image();

            var loadedImageStream = Rx.Observable
                .fromEvent(image, 'load')
                .map(function () {
                    return [image, index];
                })
                .take(1)

            image.src = imageURL;

            return loadedImageStream;
        })
        .subscribe(function (image_index) {
            var image = image_index[0];
            var index = image_index[1];

            imagesDOM.children().get(index).replaceWith(image);
        })
})