之前在写 js 代码时,想通过代码动态向 html 中插入一定数量的 js 文件,文件的依赖关系已经按顺序排好,关键代码大致如下:
var jsFiles = [‘somepath/a.js’,’somepath/b.js’,…];
var head = document.head;
jsFiles.forEach((file) => {
var script = document.createElement(‘script’);
script.type = ‘text/javascript’;
script.src = file;
head.appendChild(script);
});
但是在代码执行的过程中,很高频率的报同一个错,大概的意思就是说:b.js 在执行的时候引用的 a.js 中的方法不存在。错误出现的评率很高,但也不是 100% 出错。通过分析我觉得原因应该是这样的:虽然我是按数组中定义的顺序去动态创建 script 标签去加载对应的 js 文件,但是由于文件的大小以及网络等因素,导致各个文件现在完成的次序并不完全等于请求的次序。比如上面的例子中,如果 a.js 文件比较大,下载的比 b.js 慢,这样当 b.js 下载完成并开始执行的时候,他所依赖的 a.js 中的变量或方法就会获取不到。想到的解决方法就是,等前一个文件加载完毕之后再去加载下一个文件,大致代码如下:
function loadJsFiles(jsFiles) {
return new Promise((resolve, reject) => {
var load = function(i) {
var file = jsFiles[i];
var script = document.createElement(‘script’);
script.type = ‘text/javascript’;
script.onload = function() {
i++;
if(i === jsFiles.length) {
resolve();
} else {
load(i);
}
}
script.src = file;
head.appendChild(script);
};
load(0);
});
}
上面的方法也可以改为链式的 Promise 调用或者回调嵌套,虽然最终解决了问题,但是有一个问题,文件从异步加载变成了同步加载,增加了的文件下载的时间,文件越多的时候影响越明显。所以正确的思路应该是异步加载文件,但是执行一个文件的时候需要等到它所依赖的文件加载完毕并首先执行,对于这个问题,大家可以借鉴第三方库如 require.js, 或者 es6 中引入的模块化功能完美解决这些问题。