rxjs - RXJS 如何在成功处理错误的同时加载图像数组?
问题描述
我正在寻找预加载一堆图像,并且已经打折base64
,createObjectURL
所以我会采取更好的选择,但这就是我所拥有的。
无论如何,这就是我正在看的,一个执行此操作的函数。将一组 URLS 加载为图像。
const urls = ["lol.jpg"];
const images = urls.map((url) => {
const imageElement = document.createElement('img');
const imageComplete = fromEvent(imageElement, 'load');
imageElement.src = targetURL;
return imageComplete;
});
forkJoin(images)
但是我如何在这里正确处理加载错误?我添加了一个新的fromEvent
,但现在我有 2 个事件,而我以前只有一个,还有一个是special error case
.
const urls = ["lol.jpg"];
const images = urls.map((url) => {
const imageElement = document.createElement('img');
const imageComplete = fromEvent(imageElement, 'load');
const imageError = fromEvent(imageElement, 'error');
imageElement.src = targetURL;
return imageComplete; // <--- not good enough now
});
forkJoin(images)
在这里监听错误是否正确?最终,我需要知道其中是否有任何失败并认为它们都是失败的,但在我的测试中,404 没有catchError
任何问题,这让我想到了这个问题。
解决方案
答案部分取决于fromEvent(imageElement, 'error')
这里的作用:
fromEvent(imageElement, 'error').subscribe({
next: val => console.log("next: ", val),
error: err => console.log("error:", err)
});
如果您这样做,并且您收到一个错误,事件是触发next
还是error
?无论哪种方式,我假设您想imageElement
在加载失败时将其删除。
如果它触发error
,那么你可以这样做:
const imageComplete = fromEvent(imageElement, 'load');
const imageError = fromEvent(imageElement, 'error').pipe(
catchError(err => {
imageElement.remove();
return throwError(err);
})
);
imageElement.src = targetURL;
return merge(imageComplete, imageError);
如果它触发next
,那么您可以通过抛出如下错误将 imageError 切换到出错的流中:
const imageComplete = fromEvent(imageElement, 'load');
const imageError = fromEvent(imageElement, 'error').pipe(
tap(x => {
imageElement.remove();
throw(new Error(x.message));
})
);
imageElement.src = targetURL;
return merge(imageComplete, imageError);
现在,forkJoin
如果它的任何来源失败,它将因您的错误而失败。如果您不希望这样,则需要在错误到达您的 forkJoin 之前对其进行处理。
重试失败的图像几次
这里最棘手的一点是,您需要创建一个图像元素作为流的一部分,这样当您重试时,它不仅会重新订阅已经失败的元素的事件。
另一个棘手的问题是,您希望创建 imageElement 作为订阅流的一部分,而不是作为其定义的一部分。这defer
基本上是为你完成的。
有了这个,没有太多的变化,你可以很简单地重新尝试加载你的图像。retryWhen
如果你想让这个过程更复杂一些,你可以继续阅读(比如在重试之前延迟一点)。
const images = urls.map(targetURL =>
defer(() => of(document.createElement('img'))).pipe(
mergeMap(imageElement => {
const imageComplete = fromEvent(imageElement, 'load');
const imageError = fromEvent(imageElement, 'error').pipe(
catchError(err => {
imageElement.remove();
return throwError(err);
})
);
imageElement.src = targetURL;
return merge(imageComplete, imageError);
}),
retry(5),
catchError(err => {
console.log(`Failed to load img (${targetURL}) 5 times`);
// Throw error if you want forkJoin to fail, emit anything else
// if you want only this image to fail and the rest to keep going
return of(new DummyImage());
})
)
);
forkJoin(images);
在这种情况下,由于我们在错误命中之前处理了错误forkJoin
,我们可以预期图像数组包含DummyImage
图像加载失败 5 次的任何位置。你可以做DummyImage
任何事,真的。例如,在本地加载低分辨率默认 img。
如果你return throwError(err)
改为,那么当任何图像无法加载 5 次时,整个过程forkJoin
都会失败(尽管它仍会重试 5 次)并且你不会得到任何图像。这可能是你想要的?
推荐阅读
- jquery - 如何按类别分隔行中的特定项目,如果有一个项目具有不同的类别,则追加到新行中
- javascript - 角度路由,包括身份验证保护和重定向
- javascript - 防止重新渲染数组映射组件
- typescript - 如何将声明的条件推断类型与另一个类似的条件推断类型匹配
- c# - 检查两个具有半径的地理位置是否在彼此的半径内
- python - 如何在 unittest 测试期间添加计数器?
- python - 第三方包的 mock.patch() 的 Pytest 路径
- django - Django - 将二进制流转换为图像
- javascript - 使用钩子时如何为 panResponder 设置偏移量?
- java - 视图和投影矩阵不起作用