如何使用RxJs加载图像异步,并执行所有加载时的方法

问题描述:

我试图将我的基于承诺的代码转换为RxJs,但很难让我的头绕Rx,特别是RxJs。我有一个与路径的数组。如何使用RxJs加载图像异步,并执行所有加载时的方法

var paths = ["imagePath1","imagePath2"]; 

而且我喜欢在Javascript

var img = new Image(); 
img.src = imagePath; 
image.onload // <- when this callback fires I'll add them to the images array 

,当所有的图像加载我想执行一个方法来加载图像。

我知道有

Rx.Observable.fromArray(imagepathes) 

也有类似

Rx.Observable.fromCallback(...) 

并没有像flatMapLatest(...)Rx.Observable.interval或基于时间的调度

东西根据我的研究,我会承担这些将成为解决它的成分,但我无法让组合物起作用。

那么如何从数组路径加载图像以及何时加载所有图像我是否基于间隔执行方法?

感谢您的任何帮助。

+0

我用forkJoiin对于类似的需要(如果我正确理解你的)。 – PhiLho

我不认为你可以用observables轻松做到这一点,因为没有任何东西可以指示完成(除非你有初始大小)。查看Rx版本的其他答案。

但是,你可以使用承诺的数组:

/** 
* Loads an image and returns a promise 
* @param {string} url - URL of image to load 
* @return {Promise<Image>} - Promise for an image once finished loading. 
*/ 
function loadImageAsync(url) { 
    return new Promise(function(resolve, reject) { 
     var img = new Image(); 
     img.src = imagePath; 
     image.onload = function() { resolve(img); }; 
     image.onerror = reject; 
    }); 
} 

有了这样的,你可以很容易地做这样的事情:

var imageUrls = ['url1', 'url2', 'url3']; 
Promise.all(imageUrls.map(loadImageAsync)) 
    .then(function(arrayOfImageElements) { 
     // All done! 
    }); 
+0

即使我这样说,如果有人证明我错了,并且用Rx可观察的方式显示出来,我会认真地爱。 –

+0

难道你不能使用[startAsync](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/startasync.md)和[merge](https:// github .com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/merge.md)来实现同样的完整回调? – marekful

+0

@marekful这不是完全一样,只是增加了Rx init的开销吗? –

function loadImage(url){ 
    var img = new Image; 
    img.src = url; 
    var o = new Rx.Subject(); 
    img.onload = function(){ o.onNext(img); o.onCompleted(); }; 
    img.onerror = function(e){ o.onError(e); }; // no fromEvent for err handling 
    return o; 
} 

var imageUrls = ['url1', 'url2', 'url3']; 
var joined = Rx.Observable.merge(imageUrls.map(loadImage)); 

// consume one by one: 
joined.subscribe(function(item){ 
    // wait for item 
}); 

joined.toArray().subscribe(function(arr){ 
    // access results array in arr 
}); 

或简称:

var imageUrls = ['url1', 'url2', 'url3']; 
fromArray(imageUrls).map(url => { 
    var img = new Image; 
    img.src = url; 
    return fromEvent(img, "load"); 
}).toArray().subscribe(function(arr){ 
    // access results here 
}); 
+0

啊哈,基本上和Promises版本非常相似。 +1。 –

+0

@MadaraUchiha我刚刚在底部添加了一个'糖'版本。 –

+0

使用'Subject'是矫枉过正,如果出现错误,您将无法在'loadImage'结果中使用'retry'运算符。使用'merge'连接结果也有点不对 - 您将按加载顺序获取图像,但不会从初始url列表中获取图像。 –

首先你需要一个函数tha T将创建一个可观察或承诺为单独的图像:

function loadImage(imagePath){ 
    return Rx.Observable.create(function(observer){ 
    var img = new Image(); 
    img.src = imagePath; 
    img.onload = function(){ 
     observer.onNext(img); 
     observer.onCompleted(); 
    } 
    img.onError = function(err){ 
     observer.onError(err); 
    } 
    }); 
} 

比你可以用它来加载所有图片

Rx.Observable 
    .fromArray(imagepathes) 
    .concatMap(loadImage) // or flatMap to get images in load order 
    .toArray() 
    .subscribe(function(images){ 
    // do something with loaded images 
    }) 

其他基于RX解决方案,在这里并没有真正为我工作。 Bogdan Savluk的版本根本不起作用。本杰明Gruenbaum的版本等待,直到图像加载之前开始加载下一个图像,所以它变得非常慢(纠正我,如果我错了) 这是我的解决方案,只是比较图像的总数量已经加载的图像和如果它们相等,则返回的可观测的onNext()方法被调用与图像阵列作为参数:

var imagesLoaded = function (sources) { 

    return Rx.Observable.create(function (observer) { 

    var numImages = sources.length 
    var loaded = 0 
    var images = [] 

    function onComplete (img) { 
     images.push(img) 
     console.log('loaded: ', img) 

     loaded += 1 
     if (loaded === numImages) { 
     observer.onNext(images) 
     observer.onCompleted() 
     } 
    } 

    sources.forEach(function (src) { 
     var img = new Image() 
     img.onload = function() { 
     onComplete(img) 
     } 
     console.log('add src: ' + src) 
     img.src = src 
     if (img.complete) { 
     img.onload = null 
     onComplete(img) 
     } 

    }) 

    }) 

} 

用法:

console.time('load images'); // start measuring execution time 

imagesLoaded(sources) 
    // use flatMap to get the individual images 
    // .flatMap(function (x) { 
    // return Rx.Observable.from(x) 
    // }) 

    .subscribe(function (x) { 
    console.timeEnd('load images'); // see how fast this was 
    console.log(x) 
    })