perf(代码执行): 优化 Pyodide 引擎加载机制
- 升级 Pyodide 版本从 v0.24.1 到 v0.27.0 - 添加多 CDN 备用机制,提高加载成功率 - 加载失败时自动切换到备用 CDN - 改进加载进度提示和错误处理
This commit is contained in:
parent
79b871d203
commit
c2dcf9b23f
@ -28,14 +28,18 @@ const EXECUTION_TIMEOUT = 30000;
|
|||||||
// 最大输出长度
|
// 最大输出长度
|
||||||
const MAX_OUTPUT_LENGTH = 50000;
|
const MAX_OUTPUT_LENGTH = 50000;
|
||||||
|
|
||||||
// Pyodide CDN URL
|
// Pyodide CDN URLs(按优先级排序)
|
||||||
const PYODIDE_CDN = 'https://cdn.jsdelivr.net/pyodide/v0.24.1/full/';
|
const PYODIDE_CDN_LIST = [
|
||||||
|
'https://cdn.jsdelivr.net/pyodide/v0.27.0/full/', // jsDelivr CDN
|
||||||
|
'https://cdn.jsdelivr.net/npm/pyodide@0.27.0/', // npm CDN 备用
|
||||||
|
];
|
||||||
|
|
||||||
class PyodideEngine implements IExecutionEngine {
|
class PyodideEngine implements IExecutionEngine {
|
||||||
private pyodide: PyodideInterface | null = null;
|
private pyodide: PyodideInterface | null = null;
|
||||||
private loading = false;
|
private loading = false;
|
||||||
private loadPromise: Promise<PyodideInterface> | null = null;
|
private loadPromise: Promise<PyodideInterface> | null = null;
|
||||||
private loadingCallbacks: PyodideLoadingCallback | null = null;
|
private loadingCallbacks: PyodideLoadingCallback | null = null;
|
||||||
|
private currentCdnIndex = 0;
|
||||||
|
|
||||||
supports(language: string): boolean {
|
supports(language: string): boolean {
|
||||||
const lang = language.toLowerCase();
|
const lang = language.toLowerCase();
|
||||||
@ -120,11 +124,30 @@ class PyodideEngine implements IExecutionEngine {
|
|||||||
this.loadingCallbacks?.onLoadingProgress?.('正在加载 Python 运行时...', 0);
|
this.loadingCallbacks?.onLoadingProgress?.('正在加载 Python 运行时...', 0);
|
||||||
|
|
||||||
this.loadPromise = new Promise<PyodideInterface>(async (resolve, reject) => {
|
this.loadPromise = new Promise<PyodideInterface>(async (resolve, reject) => {
|
||||||
|
let lastError: Error | null = null;
|
||||||
|
|
||||||
|
// 尝试所有 CDN
|
||||||
|
for (let i = 0; i < PYODIDE_CDN_LIST.length; i++) {
|
||||||
|
const cdnUrl = PYODIDE_CDN_LIST[i];
|
||||||
|
this.currentCdnIndex = i;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 动态加载 Pyodide 脚本
|
// 动态加载 Pyodide 脚本
|
||||||
if (typeof window !== 'undefined' && !(window as unknown as Record<string, unknown>).loadPyodide) {
|
if (typeof window !== 'undefined') {
|
||||||
this.loadingCallbacks?.onLoadingProgress?.('正在下载 Pyodide...', 20);
|
// 移除之前可能加载失败的脚本
|
||||||
await this.loadScript(`${PYODIDE_CDN}pyodide.js`);
|
const existingScript = document.querySelector('script[data-pyodide]');
|
||||||
|
if (existingScript) {
|
||||||
|
existingScript.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置 loadPyodide 函数
|
||||||
|
delete (window as unknown as Record<string, unknown>).loadPyodide;
|
||||||
|
|
||||||
|
this.loadingCallbacks?.onLoadingProgress?.(
|
||||||
|
`正在下载 Pyodide (CDN ${i + 1}/${PYODIDE_CDN_LIST.length})...`,
|
||||||
|
20
|
||||||
|
);
|
||||||
|
await this.loadScript(`${cdnUrl}pyodide.js`);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loadingCallbacks?.onLoadingProgress?.('正在初始化 Python 环境...', 50);
|
this.loadingCallbacks?.onLoadingProgress?.('正在初始化 Python 环境...', 50);
|
||||||
@ -132,8 +155,12 @@ class PyodideEngine implements IExecutionEngine {
|
|||||||
// 初始化 Pyodide
|
// 初始化 Pyodide
|
||||||
const loadPyodide = (window as unknown as { loadPyodide: (config: { indexURL: string }) => Promise<PyodideInterface> }).loadPyodide;
|
const loadPyodide = (window as unknown as { loadPyodide: (config: { indexURL: string }) => Promise<PyodideInterface> }).loadPyodide;
|
||||||
|
|
||||||
|
if (!loadPyodide) {
|
||||||
|
throw new Error('loadPyodide 函数未加载');
|
||||||
|
}
|
||||||
|
|
||||||
const pyodide = await loadPyodide({
|
const pyodide = await loadPyodide({
|
||||||
indexURL: PYODIDE_CDN,
|
indexURL: cdnUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.loadingCallbacks?.onLoadingProgress?.('Python 环境准备就绪', 100);
|
this.loadingCallbacks?.onLoadingProgress?.('Python 环境准备就绪', 100);
|
||||||
@ -170,13 +197,27 @@ sys.stderr = __output_capture__
|
|||||||
this.loadingCallbacks?.onLoadingComplete?.();
|
this.loadingCallbacks?.onLoadingComplete?.();
|
||||||
|
|
||||||
resolve(pyodide);
|
resolve(pyodide);
|
||||||
|
return; // 成功加载,退出循环
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
lastError = error instanceof Error ? error : new Error(String(error));
|
||||||
|
console.warn(`Pyodide CDN ${i + 1} 加载失败:`, lastError.message);
|
||||||
|
|
||||||
|
// 如果还有其他 CDN,继续尝试
|
||||||
|
if (i < PYODIDE_CDN_LIST.length - 1) {
|
||||||
|
this.loadingCallbacks?.onLoadingProgress?.(
|
||||||
|
`CDN ${i + 1} 加载失败,尝试备用 CDN...`,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 所有 CDN 都失败了
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.loadPromise = null;
|
this.loadPromise = null;
|
||||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
const errorMsg = lastError?.message || '未知错误';
|
||||||
this.loadingCallbacks?.onLoadingError?.(errorMsg);
|
this.loadingCallbacks?.onLoadingError?.(errorMsg);
|
||||||
reject(new Error(`Pyodide 加载失败: ${errorMsg}`));
|
reject(new Error(`Pyodide 加载失败: ${errorMsg}`));
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.loadPromise;
|
return this.loadPromise;
|
||||||
@ -186,6 +227,7 @@ sys.stderr = __output_capture__
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.src = src;
|
script.src = src;
|
||||||
|
script.setAttribute('data-pyodide', 'true');
|
||||||
script.onload = () => resolve();
|
script.onload = () => resolve();
|
||||||
script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
|
script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
|
||||||
document.head.appendChild(script);
|
document.head.appendChild(script);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user