我正在尝试将我的基于承诺的代码转换为RxJs,但很难让我的头围绕Rx特别是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 :(得分:8)
首先,您需要一个为单独的图像创建Observable或Promise的函数:
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
})
答案 1 :(得分:1)
我不认为你可以用观察者轻松地做到这一点,因为那里没有任何东西可以指示完成(除非你有一个初始大小)。看看另一个Rx版本的答案。
但是,您可以使用Promises数组:
/**
* 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!
});
答案 2 :(得分: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
});
或简而言之:
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
});
答案 3 :(得分:0)
我认为您不必为此自己创建Observable
。
import { from, fromEvent } from 'rxjs';
import { mergeMap, map, scan, filter } from 'rxjs/operators';
const paths = ["imagePath1","imagePath2"];
from(paths).pipe(
mergeMap((path) => {
const img = new Image();
img.src = path;
return fromEvent(img, 'load').pipe(
map((e) => e.target)
);
}),
scan((acc, curr) => [...acc, curr], []),
filter((images) => images.length === paths.length)
).subscribe((images) => {
// do what you want with images
});
答案 4 :(得分:0)
这里是Angular / Typescript版本,用于使用RxJS加载图像:
import { Observable, Observer } from "rxjs";
public loadImage(imagePath: string): Observable<HTMLImageElement> {
return Observable.create((observer: Observer<HTMLImageElement>) => {
var img = new Image();
img.src = imagePath;
img.onload = () => {
observer.next(img);
observer.complete();
};
img.onerror = err => {
observer.error(err);
};
});
}
答案 5 :(得分:0)
如果您取消订阅 Observable https://stackblitz.com/edit/rxjs-loadimage?file=index.ts
import { Observable, Subscriber } from "rxjs";
/**
* RxJS Observable of loading image that is cancelable
*/
function loadImage(
url: string,
crossOrigin?: string
): Observable<HTMLImageElement> {
return new Observable(function subscriber(subscriber) {
let img = new Image();
img.onload = function onload() {
subscriber.next(img);
subscriber.complete();
};
img.onerror = function onerror(err: Event | string) {
subscriber.error(err);
};
// data-urls appear to be buggy with crossOrigin
// https://github.com/kangax/fabric.js/commit/d0abb90f1cd5c5ef9d2a94d3fb21a22330da3e0a#commitcomment-4513767
// see https://code.google.com/p/chromium/issues/detail?id=315152
// https://bugzilla.mozilla.org/show_bug.cgi?id=935069
// crossOrigin null is the same as not set.
if (
url.indexOf("data") !== 0 &&
crossOrigin !== undefined &&
crossOrigin !== null
) {
img.crossOrigin = crossOrigin;
}
// IE10 / IE11-Fix: SVG contents from data: URI
// will only be available if the IMG is present
// in the DOM (and visible)
if (url.substring(0, 14) === "data:image/svg") {
// TODO: Implement this :)
// img.onload = null;
// fabric.util.loadImageInDom(img, onLoadCallback);
}
img.src = url;
return function unsubscribe() {
img.onload = img.onerror = undefined;
if (!img.complete) {
img.src = "";
}
img = undefined;
};
});
}
// Example
const cacheBurst = new Date().getTime();
const imgUrl = `https://i.pinimg.com/originals/36/0c/62/360c628d043b2461d011d0b7f9b4d880.jpg?nocache=${cacheBurst}`;
const s = loadImage(imgUrl).subscribe(
img => {
console.log("Img", img);
},
err => {
console.log("Err", err);
}
);
setTimeout(() => {
// uncomment to check how canceling works
// s.unsubscribe();
}, 100);