2015-07-12 11 views
6

Ich versuche, meinen Versprechen basierten Code in RxJs zu konvertieren, aber habe eine harte Zeit, um mich um Rx insbesondere RxJs zu bekommen.Wie Bilder async mit RxJs laden und eine Methode ausführen, wenn alle geladen

Ich habe ein Array mit Pfaden.

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

Und Ich mag Bilder in Javascript laden

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

und wenn alle Bilder geladen sind Ich mag eine Methode auszuführen.

Ich weiß, es ist

Rx.Observable.fromArray(imagepathes) 

auch wie etwas ist

Rx.Observable.fromCallback(...) 

und es gibt so etwas wie flatMapLatest(...) Und Rx.Observable.interval oder zeitbasierten Scheduler

Auf der Grundlage meiner Forschung, die ich übernehmen würde dass dies die Zutaten sind, um es zu lösen, aber ich kann die Komposition nicht zum Funktionieren bringen.

Also, wie lade ich Bilder aus einem Array-Pfade und wenn alle Bilder geladen sind, führe ich eine Methode auf der Grundlage eines Intervalls?

Danke für jede Hilfe.

+0

I verwendet forkJoiin für einen ähnlichen Bedarf (wenn ich richtig verstanden deine). – PhiLho

Antwort

1

Ich glaube nicht, dass Sie das so leicht mit Observablen machen können, da es dort nichts gibt, was auf ein Finish hinweist (es sei denn, Sie haben eine anfängliche Größe). Schauen Sie sich die anderen Antworten für die Rx-Version an.

Sie können jedoch eine Reihe von Versprechungen verwenden:

/** 
* 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; 
    }); 
} 

Und damit können Sie leicht etwas tun:

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

Obwohl ich das gesagt habe, würde ich mich sehr freuen, wenn jemand mir einen Fehler beweisen und einen Weg mit Rx Observablen zeigen würde. –

+0

Konnte nicht [startAsync] (https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/startasync.md) und [merge] (https: // github .com/Reactive-Erweiterungen/RxJS/Blob/Master/doc/Api/Core/Operatoren/merge.md), um das gleiche mit einem vollständigen Rückruf zu erreichen? – marekful

+0

@marekful Wäre das nicht genau das Gleiche, nur mit dem zusätzlichen Overhead der Rx-Init? –

1
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 
}); 

Oder kurz:

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

Aha, also im Grunde sehr ähnlich der Promises-Version. +1. –

+0

@MadaraUchiha Ich habe gerade jetzt eine "Zucker" -Version hinzugefügt. –

+0

Die Verwendung von 'Subject' ist Overkill, auch im Falle eines Fehlers können Sie den' retry'-Operator nicht auf 'loadImage' verwenden. Die Verwendung von 'merge' zum Verketten von Ergebnissen ist auch ein bisschen falsch - Sie erhalten Bilder in der Ladereihenfolge, aber nicht in der Reihenfolge von der ursprünglichen URL-Liste. –

6

Zuerst brauchen Sie eine Funktion tha t eine beobachtbare oder Versprechen für separates Bild erstellen:

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

als Sie es verwenden, können alle Bilder Lösungen hier für mich nicht wirklich funktionieren

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

Die anderen RX Basis zu laden. Bogdan Savluks Version funktionierte überhaupt nicht.Benjamin Gruenbaums Version wartet, bis ein Bild geladen wird, bevor das nächste Bild geladen wird, so dass es sehr langsam wird (korrigiert mich, wenn ich falsch liege) Hier ist meine Lösung, die nur die Gesamtanzahl der Bilder mit der Anzahl der bereits geladenen Bilder vergleicht

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) 
     } 

    }) 

    }) 

} 

Usage:: wenn sie gleich sind, die OnNext() Methode des zurück Observable wird mit der Reihe von Bildern als ein Argument namens

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) 
    })