简体   繁体   中英

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

Today I've seen a strange problem when using RxJS. Please help me inspect it.

The problem I am working on is: "Given an array of image URLs, load and append all images to a div."

All the code snippets to demonstrate is here:

https://pastebin.com/F3ZkH3F8

At the beginning, I used the first snippet .

However, Rx.Observable.prototype.flatMap sometimes puts the images in wrong order (this behavior is noticed in the documentation). So, I changed to use concatMap ( second snippet ).

This time, only the first image is loaded. I took some time inspect the problem. I doubt that event load is not trigged from image . But the most confusing situation is that when I add some code to listen to image 's load event only, it showed me that the event is trigged properly... ( third snippet ).

Then I decided to write another version using $.Deferred ( fourth snippet ).

It worked...

Could you please tell me what is the problem? Thank you very much!

Because fromEvent(image, 'load') on the first sub-observable is not completed, other sub-observables are waiting forever. So you should complete sub-observable after first event.

Use take(1) .

excerpt from your second snippet

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

Add take(1) to complete sub-observable

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

EDIT:

Using concatMap makes loading image sequential, so it is slow.

If you pass index , you can use replace instead of append to keep the order. In this case, you can use flatMap , which enables fast concurrent loading.

$(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);
        })
})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM