异步JavaScript动态包括

时间:2015-02-06 07:52:44

标签: javascript jquery asynchronous dependency-management

我有很多JavaScript文件,以三个为例:App.js,ChildApp.js(多个)和AjaxManager.js,而AjaxManger.js是我添加的。

ChildApp扩展了App,而App依赖于AjaxManager。

在App的构造函数中:

function App() {
    this.ajaxManager=new AjaxManager();//Need AjaxManager.js to import, especially when new ChildApp is called.
    ....
}

ChildApp的构造函数:

function ChildApp's() {
    App.call(this); //
}

ChildApp.prototype = new App(); 
...

在遗留代码中,有数十个地方包含App.js ,我不希望在此之前复制并粘贴包含AjaxManager.js的相同代码。

但如果我没有很好地包含AjaxManager,那么

时会出错
var app=new ChildApp();//ReferenceError: AjaxManager is not defined

所以我使用下面的代码在App.js的开始

if(typeof AjaxManager =='undefined'){
    console.log('AjaxManager is not loaded , getting the script');
    $.ajax({
        url: .....//Url to load
        dataType: "script",
        async:false// How can I use async =true 
    });
}

但我必须对请求使用 async:false ,这会在控制台中向我显示一条警告:主线程上的同步XMLHttpRequest因其对网络的不利影响而被弃用最终用户的体验

或者我会有错误:

ReferenceError: AjaxManager is not defined

如何使用异步请求加载脚本并确保操作正常但不会破坏我现有的功能。

更新

  1. 我在App.js的创建过程中或者在函数App()的开头尝试了$.getScript(url) ,然后我才提出这个问题,但同样的错误,我猜是相对的构造函数App应放在回调中,但我不知道如何。

  2. 我希望我只能修改app.js或ajaxmanager.js ,因为有太多地方调用new App()或new ChildApp()

2 个答案:

答案 0 :(得分:1)

请勿使用async: false 。这是非常糟糕的做法,因为它会在请求完成之前阻止UI线程。

您可以使用$.getScript缩短代码,并使用回调处理程序执行依赖于正在加载的脚本的任何代码。试试这个:

if (typeof AjaxManager =='undefined') {
    $.getScript('ajaxManager.js', processPage);
}
else {
     // AjaxManager already loaded
     processPage();
}

function processPage() {
    // put your logic that relies on AjaxManager here 
}

答案 1 :(得分:0)

如果您想使用jQuery按特定顺序加载三个脚本,可以使用$.getScript()并将每个脚本链接到前一个完成的承诺中。

$.getScript('AjaxManager.js').then(function() {
    return $.getScript('App.js');
}).then(function() {
    return $.getScript('ChildApp.js');
});

或者,如果您想将其转换为可以将脚本数组传递给的函数:

function loadScripts(array) {
    return array.reduce(function(p, scriptFile) {
        return p.then(function() {
            return $.getScript(scriptFile);
        });
    }, $.Deferred().resolve().promise());
}

loadScripts('AjaxManager.js', 'App.js', 'ChildApp.js');

// The order of the scripts in the array is the order they will be loaded.

如果你想测试它们之前是否已被加载,那么你也可以为每个脚本传入一个测试符号并将其合并到加载逻辑中。

function loadScripts(array) {
    return array.reduce(function(p, obj) {
        return p.then(function() {
            if (typeof window[obj.testSymbol] === "undefined") {
                return $.getScript(scriptFile);
             } else {
                 return $.Deferred().resolve().promise();
             }
        });
    }, $.Deferred().resolve().promise());
}

loadScripts(
    {file: 'AjaxManager.js', testSymbol: 'AjaxManager'}, 
    {file: 'App.js', testSymbol: 'App'}, 
    {file: 'ChildApp.js', testSymbol: 'ChildApp'}
);    

这将允许您尝试根据需要多次加载任何给定的脚本,并且它实际上只会加载一次。


仅供参考,$.getScript()是一种异步操作。它会在完成加载或解析它返回的promise时调用您传递的回调。如果您希望某些操作仅在$.getScript()完成加载后继续,那么您必须将该代码放入回调中。

因此,如果您想在App()构造函数中加载AjaxManager脚本,则无法在构造函数返回之前执行此操作。在Javascript中不建议您在构造函数中启动异步操作,因为没有好的方法来执行错误处理,并且在异步操作完成之前可能有一个部分初始化的对象。

请参阅this answer的下半部分,了解有关构造函数中异步操作的一些注释。