Better error handling when preloading layers

I'm seeing uncaught promise rejections in `loadImage`? It's hard to know exactly where it's actually coming from, those _should_ be caught?

My guess is that it's coming from canceled images, which are throwing errors even after loading? I don't totally understand how, because looking back, I don't think the `cancel` method was actually called???

Anyway, I fixed it so cancel actually _is_ called, and that we don't throw errors when the canceled image _correctly_ fails to load.

This should be more robust either way, but hopefully it also stops the flow of errors?
This commit is contained in:
Emi Matchu 2021-06-21 09:34:05 -07:00
parent 7a8c5068a7
commit 56c91e900a
2 changed files with 11 additions and 8 deletions

View file

@ -380,10 +380,7 @@ export function usePreloadLayers(layers) {
for (const layer of layers) {
const imageAssetPromise = loadImage(
getBestImageUrlForLayer(layer, { hiResMode })
).then((image) => ({
type: "image",
image,
}));
);
imageAssetPromises.push(imageAssetPromise);
if (layer.canvasMovieLibraryUrl) {
@ -393,7 +390,6 @@ export function usePreloadLayers(layers) {
const movieAssetPromise = loadMovieLibrary(
layer.canvasMovieLibraryUrl
).then((library) => ({
type: "movie",
library,
libraryUrl: layer.canvasMovieLibraryUrl,
}));
@ -422,7 +418,7 @@ export function usePreloadLayers(layers) {
setError(e);
// Cancel any remaining promises, if cancelable.
minimalAssetPromises.forEach((p) => p.cancel && p.cancel());
imageAssetPromises.forEach((p) => p.cancel && p.cancel());
movieAssetPromises.forEach((p) => p.cancel && p.cancel());
});

View file

@ -355,10 +355,16 @@ export function useLocalStorage(key, initialValue) {
export function loadImage(rawSrc, { crossOrigin = null } = {}) {
const src = safeImageUrl(rawSrc, { crossOrigin });
const image = new Image();
let canceled = false;
const promise = new Promise((resolve, reject) => {
image.onload = () => resolve(image);
image.onerror = () =>
image.onload = () => {
if (canceled) return;
resolve(image);
};
image.onerror = () => {
if (canceled) return;
reject(new Error(`Failed to load image: ${JSON.stringify(src)}`));
};
if (crossOrigin) {
image.crossOrigin = crossOrigin;
}
@ -366,6 +372,7 @@ export function loadImage(rawSrc, { crossOrigin = null } = {}) {
});
promise.cancel = () => {
image.src = "";
canceled = true;
};
return promise;
}