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

Strange behavior of Rx.Observable.prototype.fromEvent()

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

我正在处理的问题是:"Given an array of image URLs, load and append all images to a div."

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

https://pastebin.com/F3ZkH3F8

一开始我用的是the first snippet.

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

这次只加载了第一张图片。我花了一些时间检查问题。我怀疑事件 load 不是从 image 触发的。但最令人困惑的情况是,当我添加一些代码来仅侦听 imageload 事件时,它向我显示该事件已正确触发......(third snippet)。

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

成功了...

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

因为fromEvent(image, 'load')第一个子observable没有完成,其他子observable永远在等待。所以你应该在第一个事件后完成 sub-observable。

使用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);
        })
})