如何为jQuery的$.getMultiScripts添加Deferred保证脚本顺序加载?
解决jQuery多脚本顺序加载的Firefox兼容问题
嘿,我懂你的困扰——原有的$.getMultiScripts函数是同时发起所有脚本的请求,浏览器(尤其是Firefox)会根据请求完成的先后顺序执行脚本,完全不管你传入数组的顺序。要实现严格按顺序加载执行,我们得改成串行加载:等前一个脚本加载并执行完,再去加载下一个。
正确的实现方式
这里用$.Deferred结合数组的reduce方法来构建链式调用,完美保证顺序:
$.getMultiScripts = function(arr, path) { // 处理路径前缀,可选参数 path = path || ''; if (path && path.slice(-1) !== '/') { path += '/'; } // 用reduce链式创建Deferred,实现串行加载 return arr.reduce(function(prevDeferred, scriptUrl) { return prevDeferred.then(function() { // 加载当前脚本,返回Deferred对象 return $.ajax({ cache: true, url: path + scriptUrl, dataType: 'script' }); }); }, $.Deferred().resolve()); // 初始Deferred直接resolve,启动链式流程 }; // 使用示例 var script_arr = [ 'myscript1.js', 'myscript2.js', 'myscript3.js' ]; $.getMultiScripts(script_arr, '/mypath/') .done(function() { console.log('所有脚本按顺序加载完成!'); }) .fail(function(error) { console.error('某个脚本加载失败:', error); });
关键点解释
- 串行依赖的核心:
reduce方法会遍历脚本数组,每次都在前一个脚本加载完成的then回调里发起下一个请求,从根本上保证了顺序。 - 初始Deferred:我们用
$.Deferred().resolve()作为reduce的初始值,确保第一个脚本能立刻开始加载。 - 路径兼容:保留了原函数的路径前缀功能,处理了路径末尾是否带斜杠的细节。
- 错误处理:加上
fail回调能捕获加载失败的情况,比原函数更健壮。
更简洁的版本(用$.getScript)
因为$.getScript本身就是$.ajax的封装(默认dataType: 'script'),所以可以简化代码:
$.getMultiScripts = function(arr, path) { path = path || ''; if (path && path.slice(-1) !== '/') { path += '/'; } return arr.reduce(function(prev, script) { return prev.then(function() { return $.getScript(path + script, { cache: true }); }); }, $.Deferred().resolve()); };
为什么你之前的修改没生效?
你之前尝试给所有脚本用同一个Deferred并直接resolve,这其实还是在并行发起所有请求,没有建立任何依赖关系,自然没法保证顺序。串行加载的核心是让每个请求都等待前一个完成,而不是同时触发。
这种方式在所有浏览器(包括Firefox)都能稳定工作,特别适合脚本之间有依赖关系的场景。
内容的提问来源于stack exchange,提问作者Aldee




