【问题标题】:Can I use jquery getScript() without a callback?我可以在没有回调的情况下使用 jquery getScript() 吗?
【发布时间】:2011-11-06 09:34:54
【问题描述】:

我需要在另一个 js 文件中使用来自外部 js 文件的 javascript 函数。这基本上是我尝试过的代码:

$.getScript('js/myHelperFile.js');
myHelperFunction();

这不起作用 - 我收到“未定义 myHelperFunction”的错误。

但是,此代码有效:

$.getScript('js/myHelperFile.js', function(){myHelperFunction();});

我特别希望能够以第一种方式执行此操作 - 将文件加载到我的文件顶部,然后从那里使用我需要的任何功能。这可能吗,还是我误解了 getScript 的工作原理?

【问题讨论】:

    标签: jquery getscript


    【解决方案1】:

    使用jQuery.getScript 目前无法*使用“第一种方式”,因为它只支持异步操作,因此在被调用后会立即返回。

    当脚本尝试调用myHelperFunction() 时,由于它在您的脚本下载之前返回,因此myHelperFunctionundefined 并导致该错误。

    *请参阅此答案底部的更新,了解一种有前途的新方法,最终将允许您编写几乎像您想要的样式一样工作的代码。 p>

    您可以使用jQuery.ajax 并将async 设置设置为false 并使用类似这样的代码:

    $.ajax({
      url: 'js/myHelperFile.js',
      async: false,
      dataType: "script",
    });
    
    myHelperFunction();
    

    但是正如documentation 所说:

    同步请求可能会暂时锁定浏览器,从而在请求处于活动状态时禁用任何操作。

    这是一件非常糟糕的事情。如果提供myHelperFile.js 的服务器在任何时候响应缓慢(这在某个时候发生)页面将变得无响应,直到请求完成或超时。

    最好采用在整个 jQuery 中大量使用的回调样式。

    $.getScript('js/myHelperFile.js', function () {
       myHelperFunction();
    });
    

    您也不需要使用匿名函数,您可以将代码放在命名函数中:

    function doStuffWithHelper() {
       myHelperFunction();
    }
    
    $.getScript('js/myHelperFile.js', doStuffWithHelper);
    

    如果您需要在调用myHelperFunction 之前加载多个依赖项,除非您设置某种系统来确定是否在执行代码之前下载了所有依赖项,否则这将不起作用。然后,您可以在所有 .getScript 调用上设置回调处理程序,以在运行代码之前检查所有依赖项是否已加载。

    var loadedScripts = {
        myHelperFile: false,
        anotherHelperFile: false
    };
    
    function doStuffWithHelper() {
        // check to see if all our scripts are loaded
        var prop;
        for (prop in loadedScripts) {
            if (loadedScripts.hasOwnProperty(prop)) {
                if (loadedScripts[prop] === false) {
                    return; // not everything is loaded wait more
                }
            }
        }
    
        myHelperFunction();
    }
    
    $.getScript('js/myHelperFile.js', function () {
        loadedScripts.myHelperFile = true;
        doStuffWithHelper();
    });
    $.getScript('js/anotherHelperFile.js', function () {
        loadedScripts.anotherHelperFile = true;
        doStuffWithHelper();
    });
    

    如您所见,这种方法很快就会变得复杂且难以维护。

    如果您确实需要加载多个依赖项,您最好使用诸如yepnope.js 之类的脚本加载器来执行此操作。

    yepnope( {
            load : [ 'js/myHelperFile.js', 'js/anotherHelperFile.js' ],
            complete: function () {
                var foo;
                foo = myHelperFunction();
                foo = anotherHelperFunction(foo);
                // do something with foo
            }
    } );
    

    Yepnope 像.getScript 一样使用回调,所以你不能使用你想坚持的顺序样式。这是一个很好的折衷方案,因为连续执行多个同步 jQuery.ajax 调用只会加剧该方法的问题。


    2013 年 12 月 8 日更新

    jQuery 1.5 release 提供了另一种纯粹的 jQuery 方法。从 1.5 开始,所有 AJAX 请求都返回 Deferred Object,因此您可以这样做:

    $.getScript('js/myHelperFile.js').done(function () {
       myHelperFunction();
    });
    

    作为异步请求,当然需要回调。然而,使用 Deferreds 与传统的回调系统相比具有巨大的优势,使用 $.when() 您可以轻松地将其扩展为加载多个先决条件脚本,而无需您自己的复杂跟踪系统:

    $.when($.getScript('js/myHelperFile.js'), $.getScript('js/anotherHelperFile.js')).done(function () {
      var foo;
      foo = myHelperFunction();
      foo = anotherHelperFunction(foo);
      // do something with foo
    });
    

    这将同时下载两个脚本,并且仅在它们都下载后才执行回调,就像上面的 Yepnope 示例一样。


    2014 年 3 月 30 日更新

    我最近阅读了an article,它让我意识到了一个新的 JavaScript 功能,它可能会在未来启用您想要使用的那种程序化但异步的代码! Async Functions 将使用 await 关键字暂停 async 函数的执行,直到异步操作完成。坏消息是它目前计划包含在 ES7 中,两个 ECMAScript 版本相去甚远。

    await 依赖于您正在等待返回 Promise 的异步函数。我不确定浏览器实现将如何表现,如果它们需要一个真正的 ES6 Promise 对象,或者其他承诺的实现,如从 AJAX 调用返回的 jQuery 就足够了。如果需要 ES6 Promise,则需要使用返回 Promise 的函数包装 jQuery.getScript

    "use strict";
    var getScript = function (url) {
      return new Promise(function(resolve, reject) {
        jQuery.getScript(url).done(function (script) {
          resolve(script);
        });
      });
    };
    

    然后你可以用它来下载和await,然后再继续:

    (async function () {
      await getScript('js/myHelperFile.js');
      myHelperFunction();
    }());
    

    如果您今天真的决定使用这种风格编写代码,您可以使用asycawait,然后使用Traceur 将您的代码转换为可在当前浏览器中运行的代码。这样做的额外好处是您也可以使用多个other useful new ES6 features

    【讨论】:

    • +!相当完整的答案。但是,如果您在加载 jQuery 时为什么要使用 for..in 合成器进行迭代?
    • @gion_13 它允许我从if (loadedScripts[prop] === false) { 内部返回以退出函数,因此以下依赖于依赖项的代码不会运行。如果我使用$.each,我可以用return false 提前停止循环,但是循环之后的代码会运行,我必须在循环之前设置某种标志,然后在循环之后检查它以防止这种情况从运行代码。使用for..in 循环并退出函数更干净。现在看它,而不是字典,我会使用 count var 并将其与总脚本进行比较。
    • 我明白了。我只是好奇,因为它看起来有点乱。但是你是对的,使用循环功能是不够的,你还需要通过使用某个标志来获得额外的功能。
    • 这对我不起作用,有什么想法吗? stackoverflow.com/questions/29014968/…
    【解决方案2】:

    单独使用getScript() 函数,我认为你不能。问题是正在使用 AJAX 加载脚本(实际上,getScript 只是特殊 $.ajax() 函数的简写),当浏览器尝试在下一行执行该函数时,可能服务器还没有返回文件。

    如果您不想使用回调函数,则需要使用原始形式的 jQuery $.ajax() 函数(忘记getScript()),并将传输设置为同步强>。这样,您可以确保在继续执行代码之前加载脚本:

    $.ajax({
        url: url,
        dataType: "script",
        async: false
    });
    

    【讨论】:

      【解决方案3】:

      $.getScript,实际上所有与 HTTP OP 相关的 jQuery 函数默认都是非阻塞调用。也就是说,操作完成时代码不会挂起。这意味着您上面的代码肯定会失败,因为 $.getScript 无法在尝试执行该方法的行运行之前加载脚本。这就是回调函数被接受为参数的原因。

      但是,您尝试做的事情是可能的。加载脚本后,您应该可以从任何地方调用它。为避免在下载脚本之前调用该方法所需要做的就是设置必要的标志。

      var HelperFileLoaded = false; 
      $.getScript('js/myHelperFile.js', function() { HelperFileLoaded = true; });
      
      // ...
      
      function btnSaveClick() {
          if(!HelperFileLoaded) return;
          myHelperFunction(); 
      }
      

      BenM 的建议是可行的,但除非您只对 myHelperFunction 进行一次调用,否则它不会作为解决方案起作用,因为无法保证在触发任何其他调用 myHelperFunction 的方法之前脚本已经下载。

      【讨论】:

        【解决方案4】:

        如果您使用 JSONP,您的第一个效果很好,我经常这样做。

        【讨论】:

          猜你喜欢
          • 2013-01-14
          • 1970-01-01
          • 2017-12-31
          • 2019-01-31
          • 2016-08-11
          • 2021-08-10
          • 2021-07-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多