Странное поведение Rx.Observable.prototype.fromEvent()

Сегодня я видел странную проблему при использовании RxJS. Пожалуйста, помогите мне проверить это.

Проблема, над которой я работаю: "Учитывая массив URL-адресов изображений, загрузите и добавьте все изображения в div".

Все фрагменты кода для демонстрации здесь:

https://pastebin.com/F3ZkH3F8

В начале я использовал the first snippet,

Тем не мение, Rx.Observable.prototype.flatMap иногда размещает изображения в неправильном порядке (такое поведение замечено в документации). Итак, я перешел на использование concatMap (second snippet).

На этот раз загружается только первое изображение. Я взял некоторое время, чтобы осмотреть проблему. Я сомневаюсь что событие load не запускается из image, Но самая запутанная ситуация - когда я добавляю код для прослушивания image"s load только событие, оно показало мне, что событие срабатывает правильно... (third snippet).

Тогда я решил написать другую версию, используя $.Deferred (fourth snippet).

Это сработало...

Не могли бы вы сказать мне, в чем проблема? Большое спасибо!

1 ответ

Решение

Так как fromEvent(image, 'load') на первой суб-наблюдаемой не завершена, другие суб-наблюдаемые ждут вечно. Таким образом, вы должны заполнить суб-наблюдаемый после первого события.

использование take(1),

выдержка из вашего второго фрагмента

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

Добавьте дубль (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);
        })
})
Другие вопросы по тегам