2015-07-12 9 views
6

Sto provando a convertire il mio codice basato su promessa su RxJs ma ho difficoltà a capire come Rx in particolare RxJs.Come caricare immagini asincroni con RxJ ed eseguire un metodo quando tutto è caricato

Ho una matrice con percorsi.

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

e mi piace per caricare le immagini in Javascript

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

e quando tutte le immagini sono caricate mi piace di eseguire un metodo su.

So che c'è

Rx.Observable.fromArray(imagepathes) 

c'è anche qualcosa come

Rx.Observable.fromCallback(...) 

e c'è qualcosa di simile flatMapLatest(...) E Rx.Observable.interval o scheduler timebased

Sulla base della mia ricerca Vorrei assumere che questi sarebbero gli ingredienti per risolverlo ma non riesco a far funzionare la composizione.

Quindi, come caricare le immagini da percorsi di array e quando tutte le immagini sono caricate eseguo un metodo basato su un intervallo?

Grazie per qualsiasi aiuto.

+0

ho usato forkJoiin per un'esigenza simile (se capito correttamente il tuo). – PhiLho

risposta

1

Non penso che sia possibile farlo facilmente con osservabili, poiché non c'è niente lì per indicare una finitura (a meno che non si disponga di una dimensione iniziale). Guarda le altre risposte per la versione Rx.

Tuttavia, è possibile utilizzare una serie di promesse:

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

E con questo, si può facilmente fare qualcosa di simile:

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

Anche se l'ho detto, mi piacerebbe moltissimo se qualcuno mi dimostrasse che non sbaglio e mostrassi un modo con gli osservabili Rx. –

+0

Non potresti usare [startAsync] (https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/startasync.md) e [unire] (https: // github .com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/merge.md) per ottenere lo stesso risultato con un callback completo? – marekful

+0

@marekful Non sarebbe esattamente lo stesso, solo con l'overhead aggiunto dell'iniz di Rx? –

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

o in breve:

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, quindi fondamentalmente molto simile alla versione di Promises. +1. –

+0

@MadaraUchiha Ho aggiunto una versione di 'zucchero' nella parte inferiore proprio ora. –

+0

L'uso di 'Subject' è overkill, anche in caso di errore non sarà possibile utilizzare l'operatore' retry' sul risultato 'loadImage'. Anche l'uso di "unione" per concatenare i risultati è un po 'errato: le immagini verranno caricate in ordine di caricamento, ma non in ordine dalla lista url iniziale. –

6

All'inizio è necessaria una funzione t creerà un osservabili o promessa per un'immagine separata:

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

Than è possibile utilizzarlo per caricare tutte le immagini

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

Le altre soluzioni basate RX qui in realtà non funziona per me. La versione di Bogdan Savluk non ha funzionato affatto.La versione di Benjamin Gruenbaum aspetta che venga caricata un'immagine prima di caricare l'immagine successiva in modo che diventi molto lenta (correggimi se ho torto) Ecco la mia soluzione che confronta solo la quantità totale di immagini con il numero di immagini già caricate e se sono uguali, il metodo onNext() dell'osservabile restituito viene chiamato con la serie di immagini come argomento:

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

    }) 

    }) 

} 

utilizzati:

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