fix(web): recover transient map SDK failures

This commit is contained in:
lingniu
2026-07-16 01:09:56 +08:00
parent e80e1dcea9
commit b93e165590
2 changed files with 76 additions and 15 deletions

View File

@@ -1,5 +1,18 @@
import { expect, test } from 'vitest'; import { afterEach, expect, test, vi } from 'vitest';
import { buildAMapMarkerURL, gcj02ToWgs84, isValidAMapCoordinate, wgs84ToGcj02 } from './amap'; import {
buildAMapMarkerURL,
gcj02ToWgs84,
isValidAMapCoordinate,
loadAMap,
type AMapLike,
wgs84ToGcj02
} from './amap';
afterEach(() => {
delete window.__LINGNIU_APP_CONFIG__;
delete window.AMapLoader;
document.querySelector('script[src="https://webapi.amap.com/loader.js"]')?.remove();
});
test('accepts coordinates inside the China vehicle service range', () => { test('accepts coordinates inside the China vehicle service range', () => {
expect(isValidAMapCoordinate(116.397128, 39.916527)).toBe(true); expect(isValidAMapCoordinate(116.397128, 39.916527)).toBe(true);
@@ -30,3 +43,31 @@ test('keeps coordinates outside China unchanged and converts AMap marker links a
expect(position[0]).toBeCloseTo(116.40337, 5); expect(position[0]).toBeCloseTo(116.40337, 5);
expect(position[1]).toBeCloseTo(39.91793, 5); expect(position[1]).toBeCloseTo(39.91793, 5);
}); });
test('retries the SDK load after a transient AMap loader rejection', async () => {
const amap = {} as AMapLike;
const load = vi.fn()
.mockRejectedValueOnce(new Error('temporary network failure'))
.mockResolvedValueOnce(amap);
window.__LINGNIU_APP_CONFIG__ = { amapWebJsKey: 'amap-web-key' };
window.AMapLoader = { load };
await expect(loadAMap(['AMap.Geocoder'])).rejects.toThrow('temporary network failure');
await expect(loadAMap(['AMap.Geocoder'])).resolves.toBe(amap);
expect(load).toHaveBeenCalledTimes(2);
});
test('removes a failed loader script so a later request can recover', async () => {
const amap = {} as AMapLike;
window.__LINGNIU_APP_CONFIG__ = { amapWebJsKey: 'amap-web-key' };
const firstAttempt = loadAMap(['AMap.ToolBar']);
const failedScript = document.querySelector<HTMLScriptElement>('script[src="https://webapi.amap.com/loader.js"]');
expect(failedScript).not.toBeNull();
failedScript?.dispatchEvent(new Event('error'));
await expect(firstAttempt).rejects.toThrow('高德地图 Loader 加载失败');
expect(document.querySelector('script[src="https://webapi.amap.com/loader.js"]')).toBeNull();
window.AMapLoader = { load: vi.fn().mockResolvedValue(amap) };
await expect(loadAMap(['AMap.ToolBar'])).resolves.toBe(amap);
});

View File

@@ -104,16 +104,31 @@ function loadScript(src: string) {
} }
const existing = document.querySelector<HTMLScriptElement>(`script[src="${src}"]`); const existing = document.querySelector<HTMLScriptElement>(`script[src="${src}"]`);
if (existing) { if (existing) {
if (existing.dataset.loadState === 'loaded') {
existing.remove();
loadScript(src).then(resolve, reject);
return;
}
existing.addEventListener('load', () => resolve(), { once: true }); existing.addEventListener('load', () => resolve(), { once: true });
existing.addEventListener('error', () => reject(new Error('高德地图 Loader 加载失败')), { once: true }); existing.addEventListener('error', () => {
existing.remove();
reject(new Error('高德地图 Loader 加载失败'));
}, { once: true });
if (window.AMapLoader) resolve(); if (window.AMapLoader) resolve();
return; return;
} }
const script = document.createElement('script'); const script = document.createElement('script');
script.src = src; script.src = src;
script.async = true; script.async = true;
script.onload = () => resolve(); script.dataset.loadState = 'loading';
script.onerror = () => reject(new Error('高德地图 Loader 加载失败')); script.onload = () => {
script.dataset.loadState = 'loaded';
resolve();
};
script.onerror = () => {
script.remove();
reject(new Error('高德地图 Loader 加载失败'));
};
document.head.appendChild(script); document.head.appendChild(script);
}); });
} }
@@ -221,7 +236,8 @@ export function loadAMap(plugins: AMapPlugin[] = ['AMap.Scale']) {
} else if (config.securityJsCode) { } else if (config.securityJsCode) {
window._AMapSecurityConfig = { securityJsCode: config.securityJsCode }; window._AMapSecurityConfig = { securityJsCode: config.securityJsCode };
} }
const promise = loadScript('https://webapi.amap.com/loader.js').then(() => { const promise = loadScript('https://webapi.amap.com/loader.js')
.then(() => {
if (!window.AMapLoader) { if (!window.AMapLoader) {
throw new Error('高德地图 Loader 不可用'); throw new Error('高德地图 Loader 不可用');
} }
@@ -230,6 +246,10 @@ export function loadAMap(plugins: AMapPlugin[] = ['AMap.Scale']) {
version: '2.0', version: '2.0',
plugins plugins
}) as Promise<AMapLike>; }) as Promise<AMapLike>;
})
.catch((error: unknown) => {
amapLoaderPromises.delete(key);
throw error;
}); });
amapLoaderPromises.set(key, promise); amapLoaderPromises.set(key, promise);
return promise; return promise;