You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

jQuery中如何处理并行脚本加载时相同资源的等待问题

问题解决方案

问题根因

原实现仅使用window.loadedScripts数组记录已加入加载队列的脚本地址,没有区分「加载中」和「已加载完成」两种状态,也没有存储对应脚本的加载等待对象,导致并发请求同一个未完成加载的脚本时,后续请求会直接判定为已加载,提前触发回调,出现依赖未加载完成的异常。

解决思路

通过全局Map存储每个脚本对应的加载Deferred对象,所有请求同一脚本的任务都会复用同一个Deferred等待加载结果,仅第一次请求会发起真实加载,加载完成后统一触发所有等待该脚本的任务回调。

完整修改后代码

// Function to load Scripts on the fly
$.loadScript = function(url, arg1, arg2) {
    var cache = false,
        callback = null;
    //arg1 and arg2 can be interchangable
    if ($.isFunction(arg1)) {
        callback = arg1;
        cache = arg2 || cache;
    } else {
        cache = arg1 || cache;
        callback = arg2 || callback;
    }
    
    var that = this;
    // 初始化全局脚本加载状态存储,key为脚本url,value为对应加载Deferred
    window.scriptLoadMap = window.scriptLoadMap || new Map();
    // 保留原全局数组兼容其他可能的依赖,无需兼容可删除
    window.loadedScripts = window.loadedScripts || [];

    var deferred = jQuery.Deferred();
    if (jQuery.isFunction(callback)) {
        deferred.done(function(){
            callback.call(that);
        });
    };

    if( url.constructor === Array ){
        function loadScript(i) {
            if (i < url.length) {
                var el = url[i];
                var existDeferred = window.scriptLoadMap.get(el);
                // 已有加载任务,直接等待完成后加载下一个
                if (existDeferred) {
                    existDeferred.done(() => loadScript(i + 1));
                    return;
                }
                // 第一次加载,创建全局Deferred存储
                var scriptDeferred = jQuery.Deferred();
                window.scriptLoadMap.set(el, scriptDeferred);
                // 发起加载请求
                $.ajax({
                    url: el,
                    success: function(e){
                        __info('Loaded script. url: '+el, 'verbose');
                        window.loadedScripts.push(el);
                        // 标记脚本加载完成
                        scriptDeferred.resolve();
                        loadScript(i + 1);
                    },
                    complete: function(e){
                        if(typeof e.done !== 'function'){
                            $(function () {
                                $('<script>')
                                    .attr('type', 'text/javascript')
                                    .text(e.responseText)
                                    .prop('defer',true)
                                    .appendTo('head');
                            })
                        }
                    },
                    dataType: 'script',
                    cache: cache
                });
            } else {
                deferred.resolve();
            }
        }
        loadScript(0);
        return deferred;
    } else {
        var existDeferred = window.scriptLoadMap.get(url);
        // 已有加载任务,等待完成后resolve当前请求
        if (existDeferred) {
            existDeferred.done(() => deferred.resolve());
            return deferred;
        }
        // 第一次加载,创建全局Deferred存储
        var scriptDeferred = jQuery.Deferred();
        window.scriptLoadMap.set(url, scriptDeferred);
        $.ajax({
            url: url,
            success: function(e){
                __info('Loaded script. url: '+url, 'verbose');
                window.loadedScripts.push(url);
                // 标记脚本加载完成
                scriptDeferred.resolve();
                deferred.resolve();
            },
            complete: function(e){
                if(typeof e.done !== 'function'){
                    $(function () {
                        $('<script>')
                            .attr('type', 'text/javascript')
                            .text(e.responseText)
                            .prop('defer',true)
                            .appendTo('head');
                    })
                }
            },
            dataType: 'script',
            cache: cache
        });
    }
    return deferred;
};

核心修改说明

  • 新增全局scriptLoadMap存储结构,用于记录每个脚本url对应的加载Deferred对象,区分「加载中」和「已完成」状态
  • 单脚本/批量脚本加载时,优先查询全局存储:如果已有对应脚本的加载Deferred,直接绑定当前请求的后续逻辑到该Deferred的完成回调上,无需重复发起请求,也不会提前结束
  • 仅第一次请求某脚本时会发起真实AJAX加载,加载完成后触发所有等待该脚本的请求的后续逻辑
  • 保留原有loadedScripts数组、缓存逻辑、脚本注入逻辑,完全兼容原有使用方式

内容的提问来源于stack exchange,提问作者Fran Díaz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.11 09:51:03