resource-preloader 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -0
- package/dist/resource-loader.amd.js +309 -0
- package/dist/resource-loader.amd.js.map +1 -0
- package/dist/resource-loader.amd.min.js +309 -0
- package/dist/resource-loader.amd.min.js.map +1 -0
- package/dist/resource-loader.cjs.js +307 -0
- package/dist/resource-loader.cjs.js.map +1 -0
- package/dist/resource-loader.cjs.min.js +307 -0
- package/dist/resource-loader.cjs.min.js.map +1 -0
- package/dist/resource-loader.esm.js +300 -0
- package/dist/resource-loader.esm.js.map +1 -0
- package/dist/resource-loader.esm.min.js +300 -0
- package/dist/resource-loader.esm.min.js.map +1 -0
- package/dist/resource-loader.umd.js +313 -0
- package/dist/resource-loader.umd.js.map +1 -0
- package/dist/resource-loader.umd.min.js +313 -0
- package/dist/resource-loader.umd.min.js.map +1 -0
- package/package.json +51 -0
- package/src/config.js +30 -0
- package/src/index.js +13 -0
- package/src/loader.js +116 -0
- package/src/scheduler.js +158 -0
package/src/scheduler.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { getGlobalConfig } from './config.js';
|
|
2
|
+
import { getLoader } from './loader.js';
|
|
3
|
+
import { wrapLoadPromise } from './loader.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 检测循环依赖(深度优先遍历,配置字段改为dependencies)
|
|
7
|
+
* @param {Array} configList - 完整配置数组
|
|
8
|
+
* @param {number} currentId - 当前资源ID
|
|
9
|
+
* @param {Set} visited - 已访问的资源ID集合
|
|
10
|
+
* @param {Set} visiting - 正在访问的资源ID集合(用于检测环)
|
|
11
|
+
*/
|
|
12
|
+
function detectCycle(configList, currentId, visited, visiting) {
|
|
13
|
+
const currentConfig = configList.find(item => item.name === currentId);
|
|
14
|
+
if (!currentConfig) {
|
|
15
|
+
throw new Error(`未找到ID为${currentId}的资源配置`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// 若正在访问中,说明存在循环依赖
|
|
19
|
+
if (visiting.has(currentId)) {
|
|
20
|
+
throw new Error(`检测到循环依赖:${Array.from(visiting).join(' -> ')} -> ${currentId}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 若已访问过,直接返回(无需重复检测)
|
|
24
|
+
if (visited.has(currentId)) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 标记为正在访问
|
|
29
|
+
visiting.add(currentId);
|
|
30
|
+
// 递归检测依赖项(改为dependencies)
|
|
31
|
+
const dependencies = currentConfig.dependencies || [];
|
|
32
|
+
for (const depId of dependencies) {
|
|
33
|
+
detectCycle(configList, depId, visited, visiting);
|
|
34
|
+
}
|
|
35
|
+
// 标记为已访问,移出正在访问集合
|
|
36
|
+
visiting.delete(currentId);
|
|
37
|
+
visited.add(currentId);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 预检测所有资源的循环依赖(配置字段改为dependencies)
|
|
42
|
+
* @param {Array} configList - 完整配置数组
|
|
43
|
+
*/
|
|
44
|
+
function checkAllCycles(configList) {
|
|
45
|
+
const visited = new Set();
|
|
46
|
+
for (const config of configList) {
|
|
47
|
+
detectCycle(configList, config.name, visited, new Set());
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 递归加载单个资源及其所有依赖(保证依赖先加载,配置字段改为dependencies)
|
|
53
|
+
* @param {number} resourceId - 资源ID
|
|
54
|
+
* @param {Array} configList - 完整配置数组
|
|
55
|
+
* @param {number} timeout - 超时时间
|
|
56
|
+
* @param {Map} loadedMap - 已加载资源的结果缓存
|
|
57
|
+
*/
|
|
58
|
+
async function loadResourceWithDeps(resourceId, configList, timeout, loadedMap) {
|
|
59
|
+
// 若已加载,直接返回缓存结果
|
|
60
|
+
if (loadedMap.has(resourceId)) {
|
|
61
|
+
return loadedMap.get(resourceId);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const currentConfig = configList.find(item => item.name === resourceId);
|
|
65
|
+
if (!currentConfig) {
|
|
66
|
+
throw new Error(`未找到ID为${resourceId}的资源配置`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 1. 先加载所有依赖(改为dependencies)
|
|
70
|
+
const dependencies = currentConfig.dependencies || [];
|
|
71
|
+
for (const depId of dependencies) {
|
|
72
|
+
await loadResourceWithDeps(depId, configList, timeout, loadedMap);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 2. 再加载当前资源的urls(顺序加载,一个成功即可)
|
|
76
|
+
const { urls, type } = currentConfig;
|
|
77
|
+
let loadResult = null;
|
|
78
|
+
let status = 'success';
|
|
79
|
+
let error = null;
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
loadResult = await loadUrlsInOrder(urls, type, timeout);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
status = 'failed';
|
|
85
|
+
error = err;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 3. 缓存加载结果
|
|
89
|
+
const result = {
|
|
90
|
+
resourceId,
|
|
91
|
+
config: currentConfig,
|
|
92
|
+
loadResult,
|
|
93
|
+
status,
|
|
94
|
+
error,
|
|
95
|
+
};
|
|
96
|
+
loadedMap.set(resourceId, result);
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 按顺序加载urls,只需要一个成功即可
|
|
102
|
+
* @param {Array} urls - 资源地址数组
|
|
103
|
+
* @param {string} type - 资源类型
|
|
104
|
+
* @param {number} timeout - 超时时间
|
|
105
|
+
* @returns {Promise<Object>} 第一个成功的加载结果
|
|
106
|
+
*/
|
|
107
|
+
async function loadUrlsInOrder(urls, type, timeout) {
|
|
108
|
+
if (!Array.isArray(urls) || urls.length === 0) {
|
|
109
|
+
throw new Error('urls必须是非空数组');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const loader = getLoader(type);
|
|
113
|
+
let lastError;
|
|
114
|
+
|
|
115
|
+
// 按顺序遍历urls,直到有一个加载成功
|
|
116
|
+
for (const url of urls) {
|
|
117
|
+
try {
|
|
118
|
+
return await wrapLoadPromise(url, loader, timeout);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
lastError = error;
|
|
121
|
+
continue; // 加载失败,继续下一个url
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 所有url都加载失败,抛出最后一个错误
|
|
126
|
+
throw new Error(`所有${type}类型资源加载失败,最后一个错误:${lastError?.message || '未知错误'}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 核心加载调度方法(最终结果保持配置定义顺序)
|
|
131
|
+
* @param {Array} configList - 输入配置数组
|
|
132
|
+
* @returns {Promise<Array>} 按配置定义顺序的加载结果数组
|
|
133
|
+
*/
|
|
134
|
+
async function preloadResources(configList) {
|
|
135
|
+
if (!Array.isArray(configList) || configList.length === 0) {
|
|
136
|
+
throw new Error('配置数组必须是非空数组');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 1. 获取全局配置
|
|
140
|
+
const { timeout: globalTimeout } = getGlobalConfig();
|
|
141
|
+
|
|
142
|
+
// 2. 预检测所有循环依赖(防止无限递归)
|
|
143
|
+
checkAllCycles(configList);
|
|
144
|
+
|
|
145
|
+
// 3. 初始化缓存(存储已加载资源结果,避免重复加载)
|
|
146
|
+
const loadedMap = new Map();
|
|
147
|
+
|
|
148
|
+
// 4. 按配置定义的顺序,并行加载同层级无依赖资源(保证依赖先加载,结果保留原始顺序)
|
|
149
|
+
const rawOrderPromises = configList.map(async (config) => {
|
|
150
|
+
return await loadResourceWithDeps(config.name, configList, globalTimeout, loadedMap);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// 5. 等待所有资源加载完成,返回原始配置顺序的结果
|
|
154
|
+
const finalResult = await Promise.all(rawOrderPromises);
|
|
155
|
+
return finalResult;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export { preloadResources };
|