【问题标题】:Using jQuery load with promises使用带有承诺的 jQuery 加载
【发布时间】:2011-09-24 07:57:11
【问题描述】:

我仍在努力解决deferred 的问题,所以考虑到这一点,我有一个关于如何执行以下操作的问题。

我和我的团队有 3 个独立的 .load() 方法,每个方法都获取一个特定的模板并将其附加到同一个容器中。如您所想,每次加载所花费的时间不同,因此当内容加载时,它会以“阶梯式”方式加载(1,然后 2,然后 3)。我想使用deferred objects 并等待它们全部完成,然后同时附加它们以删除“楼梯”动作。

$('<div>').load(baseInfoTemplate, function () {
    var baseData = {
        // build some object
    };

    $.tmpl(this, baseData).appendTo($generalContainer);
});

所有三个调用都与上面的调用类似。

我怎样才能做到这一点?

【问题讨论】:

  • 附加它们的顺序重要吗?
  • 如果三个模板中的每一个都有自己的自己的容器,而不是将它们各自附加到同一个容器中,那么解决方案会更容易。
  • 顺序确实很重要...我同意如果他们有自己的就好了:)
  • 你能做到吗:&lt;div id="c"&gt; &lt;div id="part1"/&gt; &lt;div id="part2"/&gt; ... &lt;/div&gt;

标签: jquery jquery-deferred jquery-load promise


【解决方案1】:

我使用以下代码作为通用库

var loader = {};
(function(){
  var fn = {  
    promises: [],
    templates: [],

    loadTemplate: function( name ) {
      fn.promises.push(
        $.get( `templates/${name}.tmpl.html`,
               (html) => fn.templates.push( html )
        )
      );
    },

    main: function( templates, callback ) {
      templates.forEach( (template) => fn.loadTemplate( template ));
      $.when.apply( $, fn.promises ).done( function() {
        $( '<div id="templates">' ).html( fn.templates.join() ).appendTo( 'body' );
        callback();
      });
    }
  };

  /* EXPORTS */
  loader.main = fn.main;
})();

然后将其作为应用程序主 js 文件中的第一个函数调用。

function myMain() {
  ... // do all the things
}

$( document ).ready( function() {
  templates = [ 'thingies', 'wotsits', 'oojamaflips' ];
  loader.main( templates, () => myMain());
});

【讨论】:

    【解决方案2】:

    Here's a Gist 用于向一组 jQuery 元素添加 loadThen 函数的小 jQuery 插件。它基本上是 load() 没有回调,它返回一个仅在内容加载并插入到选定元素集中后才解析的承诺。

    它基本上是 jQuery 自己的 load() 代码的复制/粘贴,除了它从实际的 ajax 调用返回承诺。如果 ajax 失败,这可以让你得到一个被拒绝的承诺。

    由于它基于 load() 功能,您可以在 url 后添加一个选择器,以空格分隔,以仅获取加载的 html 的片段。


    示例1:将该站点的主页加载到id="container"的元素中

    $('#container').loadThen('/').then(function () {
        // loaded and ready.
    }, function () {
        // error
    });
    

    示例2:将首页的页眉加载到本页的页眉中

    $('h1').eq(0).loadThen('/ h1').then(function () {
        // loaded and ready.
    }, function () {
        // error
    });
    

    要点内容:

    (function ($) {
        var _loadThen = $.fn.loadThen;
        $.fn.loadThen = function (url, params) {
            if (typeof url !== "string" && _loadThen) {
                return _loadThen.apply(this, arguments);
            }
    
            if(this.length <= 0) {
                return jQuery.Deferred().resolveWith(this, ['']);
            }
    
            var selector, type, response,
                self = this,
                off = url.indexOf(" ");
    
            if (off >= 0) {
                selector = jQuery.trim(url.slice(off));
                url = url.slice(0, off);
            }
    
            if (params && typeof params === "object") {
                type = "POST";
            }
    
            return jQuery.ajax({
                url: url,
                type: type,
                dataType: "html",
                data: params
            }).then(function (responseText) {
                    self.html(selector ? jQuery("<div>").append(jQuery.parseHTML(responseText)).find(selector) : responseText);
                return self;
            });
        };
    }(jQuery));
    

    【讨论】:

      【解决方案3】:

      我在这种情况下使用下一个代码:

      $.when(
          $.get('templates/navbar.tmpl.html', function(data) {
              $('#navbar').html(data);
          }),
          $.get('templates/footer.tmpl.html', function(data) {
              $('#footer').html(data);
          }),
          $.Deferred(function(deferred) {
              $(deferred.resolve);
          })
      ).done(function() {
          $.getScript("js/tools/jquery.min.js");
      });
      

      在我看来,它看起来比以前的实现更有条理和漂亮。

      【讨论】:

      • 是的,我喜欢能够看到我正在加载的模板,很好的解决方案
      【解决方案4】:

      您可以将promise objects 存储在一个数组中,并使用$.when() 来确定这些承诺是否已履行。这可能如下所示:

      var templates = [ ];
      
      function createPromise( baseInfoTemplate ) {
          return $.Deferred(function( promise ) {
              $('<div>').load(baseInfoTemplate, function() {
                  var baseData = { /* foobar */ };
      
                  templates.push( baseData );
                  promise.resolve();
              });
          }).promise();
      }
      
      var myPromises = [ ];
      
      myPromises.push( createPromise( 'some data' ) );
      myPromises.push( createPromise( 'even moar data' ) );
      myPromises.push( createPromise( 'foo bar heay' ) );
      
      $.when.apply( null, myPromises ).done( function() {
          templates.forEach(function( template ) {
              $.tmpl(this, template).appendTo($generalContainer);
          });
      });
      

      我在这里使用.apply(),因为它接受一个数组作为函数调用的参数。所以基本上,我们将所有的 Promise 对象传递给 .when()

      示例http://jsfiddle.net/Hg77A/1/


      更新:

      正如 Alnitak 所指出的,如果没有“成功”回调处理程序,上述示例将没有多大意义。如果在您使用.load() 传输数据后触发“全部完成”处理程序就足够了,您只需要.resolve() 来自.load()success handler 中的promise。这有意义吗?

      【讨论】:

      • 这似乎不起作用 - 所有的片段都一次渲染一个
      • @Alnitak:是的。我对.tmpl() 不是很熟悉,但我希望它提供某种“完成回调”,您可以在其中解决一个promise 对象。如果那不存在,那么使用 Promise 就没有任何意义。好点子。
      • 仍然可以使用 Deferred 对象,但是如果三个模板必须以正确的顺序放置在适当的位置,这并不容易。
      • 并且无论如何$.load() 确实 有一个成功处理程序,但是您的代码将模板呈现为 each 一个完成,而不是当他们'都完成了。
      • 更好的方法是使用带有 Promise 的 jQuery AJAX 看看gist.github.com/Tusko/dfe1fcbbb71fc0a36c86cdd79530d1fc
      【解决方案5】:

      $.load() 不是为与 Deferred 对象一起使用而设计的,它还专门用于将内容立即放入页面中。

      要解决后一个问题,您要么必须在 DOM 外部渲染整个容器,然后在它们全部完成后将其放入,要么您需要累积三个结果,然后一次性将它们全部放入。

      下面的过程使用后一种方法:

      1. 改用$.get(),并创建由$.get()返回的jqXHR对象的数组

      2. 将每个$.get()的返回片段也存储在一个数组中

      3. 使用$.when.apply($, myArray).done(function() { ... }) 应用模板并将它们放入DOM

      http://jsfiddle.net/alnitak/WW3ja/

      【讨论】:

      • 使用 .get() 的唯一问题是我无法使用 load() 使用哈希加载页面片段...
      • @Mike 对,所以选择选项 1,但将模板渲染到屏幕外的 DIV 中。然后在最终的$.done() 回调中将 that DIV 添加到页面。
      • 我喜欢这个想法,但 load() 不返回 jqXHR 对象,它只返回 jQuery。所以 .done() 立即触发而不是等待。我是否必须切换到 .get() 并将模板移动到自己的文件中才能实现我的目标?
      • @Mike no - 您可以为每个$.load() 创建一个$.Deferred(),然后将def[n].resolve 设置为$.load() 中的完成回调。为每个模板设置一个单独的 div 也会更好。
      • @Alnitak 主要是 :) 我决定将模板移动到他们自己的文件中并使用 $.get(),一旦我这样做了,它就很好地解决了......感谢您的帮助!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-04-06
      • 2017-01-17
      • 1970-01-01
      • 2012-12-19
      • 2015-08-06
      • 1970-01-01
      • 2015-08-28
      相关资源
      最近更新 更多