1

我有一个基于 apache、php、js/jquery 的网络应用程序。加载文档时初始化应用程序的一个模块。该序列调用一个执行许多任务的 init() 函数,除其他外,它通过 ajax 获取两个 html 对话框并将它们插入到现有页面中。对这些对话框的引用保存为变量,因此我不必在整个脚本中指定 jquery 选择器,而是可以简单地使用这些变量。这工作正常,除了在尝试将处理程序绑定到获取的对话框内的元素时变量“未定义”的情况很少(偶尔......)。这很奇怪,因为除了绑定之外,对话框在整个应用程序中都是可用的。这表明 ajax 调用成功,但显然存在某种竞争条件,绑定尝试之后。

根据我的理解,这不应该发生,因为绑定是在 when() 的 .done() 部分完成的,并且只能在对话框的 ajax 检索在 when() 内完成后执行。

显然我在这里遗漏了一些基本的东西。有人对我有提示吗?

以下引用了工作实现的代码摘录。它们可能作为某些部分在语法上看起来无效,但这只是由于删除了与此处无关的代码部分。未缩短的代码工作正常。

变量:

Shorty.Tracking.Dialog.List:{};
Shorty.Tracking.Dialog.Click:{};

初始化顺序:

$(window).load(function(){
  //...
  var dfd = new $.Deferred();
  $.when(
    // load layout of dialog to show the list of tracked clicks
    Shorty.Tracking.init()
  ).done(function(){
    // bind actions to basic buttons
    Shorty.Tracking.Dialog.List.find('#close').on('click',function(){
      // ...
    });
    // ...
  });
  // ...
})

缩短的初始化函数:

Shorty.Tracking.init:function(){
    // ...
    var dfd=new $.Deferred();
    // two dialogs are used by this plugin
    var dialogs={
      'list':  Shorty.Tracking.Dialog.List,
      'click': Shorty.Tracking.Dialog.Click
    };
    // load dialogs from server
    $.each(['list','click'],function(i,dialog){
      if (...){
        // ...
        dfd.resolve();
      }else{
        // load dialog layout via ajax and create a freshly populated dialog
        $.ajax({
          type:     'GET',
          url:      OC.filePath('shorty-tracking','ajax','layout.php'),
          data:     { dialog: dialog},
          cache:    false,
          dataType: 'json'
        }).pipe(
          function(response){return Shorty.Ajax.eval(response)},
          function(response){return Shorty.Ajax.fail(response)}
        ).done(function(response){
          // create a fresh dialog
          // insert it alongside the existing dialogs in the top controls bar
          $('#controls').append(response.layout);
          switch (dialog){
            case 'list':
              Shorty.Tracking.Dialog.List=$('#controls #shorty-tracking-list-dialog').first();
              break;
            case 'click':
              Shorty.Tracking.Dialog.Click=$('#controls #shorty-tracking-click-dialog').first();
          } // switch
          dfd.resolve();
        }).fail(dfd.reject)
      } // else
    }); // each
    return dfd.promise();
  },

随着Bergi的回答,我设法显然消除了原来的问题。到目前为止,我无法检测到更多失败的绑定尝试。但是我不能完全遵循这个建议:在他的回答中,他建议删除初始化方法中的 switch 语句以支持直接分配。这当然要优雅得多,但这行不通。我的印象是我对 javascript 如何处理存储在变量中的引用和/或函数存在一些误解。

也许您、Bergi 或其他人可以对此做出一些解释:

这是上面修改后的初始化方法:

  init:function(){
    if (Shorty.Debug) Shorty.Debug.log("initializing tracking list");
    // check if dialogs already exist
    if (   $.isEmptyObject(Shorty.Tracking.Dialog.List)
        && $.isEmptyObject(Shorty.Tracking.Dialog.Click) ){
      // two dialogs are used by this plugin
      var dialogs={
        'list':  Shorty.Tracking.Dialog.List,
        'click': Shorty.Tracking.Dialog.Click
      };
      // load dialogs from server
      var dfds=$.map(dialogs,function(obj,dialog){
        // load dialog layout via ajax and append it to the collection of dialogs in the controls
        return $.ajax({
          type:     'GET',
          url:      OC.filePath('shorty-tracking','ajax','layout.php'),
          data:     { dialog: dialog},
          cache:    false,
          dataType: 'json'
        }).pipe(
          function(response){return Shorty.Ajax.eval(response)},
          function(response){return Shorty.Ajax.fail(response)}
        ).done(function(response){
          // create a fresh dialog and insert it alongside the existing dialogs in the top controls bar
          $('#controls').append(response.layout);
//           obj=$('#controls #shorty-tracking-'+dialog+'-dialog').first();
          switch(dialog){
            case 'list':
              Shorty.Tracking.Dialog.List=$('#controls #shorty-tracking-list-dialog').first();
              break;
            case 'click':
              Shorty.Tracking.Dialog.Click=$('#controls #shorty-tracking-click-dialog').first();
              break;
          } // switch
        })
      }) // map
      return $.when.apply(null, dfds);
    }else{
      // dialogs already loaded, just clean them for usage
      Shorty.Tracking.Dialog.List.find('#list-of-clicks tbody tr').remove();
      new Deferred().resolve();
    } // else
  },

在中间,您会看到赋值语句被注释掉并且当前被下面的 switch 语句替换。我尝试了几种变体,例如 obj.element 等,但都失败了。在函数之外,变量 Shorty.Tracking.Dialog.List 和 Shorty.Tracking.-Dialog.Click 保持为空。

我是 web 东西和 js/jquery 的绝对新手。但我当然很想了解更多关于这种处理引用的方式。

4

1 回答 1

2

这是有问题的代码:

var dfd=new $.Deferred();
$.each(['list','click'], function(){
    ...
    dfd.resolve/fail();
});

您只创建了一个 Deferred,但会多次解决它。所以当第一个解决方案发生时,第二个 Ajax 就不会完成。

因此,改为使用两个 Deferred,并通过jQuery.when(). 另外,我认为您不需要自己创建 Deferreds。就拿你从pipe()电话中得到的那两个。

function init() {
   var dialogs={
      'list':  Shorty.Tracking.Dialog.List,
      'click': Shorty.Tracking.Dialog.Click
   };
   var deferreds = $.map(dialogs, function(obj, dialog) {
       return $.ajax(...).pipe(...).done(...
           // really, don't use switch here:
           obj.element = response.layout;
       }); // done() returns the Deferred
   });
   return $.when.apply(null, deferreds);
}

...

$.ready(function($) {
    init().done(...);
});

好的,我需要解释一下 switch-Statement。确实,dialogs对象中的值将从 [未初始化] 变量分配并保持该值 ( undefined),当重新分配字段时,Shorty值不会改变。Javascript 中没有“指针”。dialog我认为只有在成员运算符中使用当前变量而不是 switch 语句才会有用。要分配给Shorty.Tracking.Dialog.Listand .Click,您需要使用类似的东西

var dialogs = ["list", "click"];
var shortcut = Shorty.Tracking.Dialog; // some object
for (var i=0; i<dialogs.length; i++) {
    // loop over them,
    // do the ajax stuff
    var element = $(repsonse.layout); // get the added element from the response
    $('#controls').append(element);

    shortcut[dialogs[i]] = element;
}

(最终使用How do I make the first letter of a string of a string in JavaScript? 中的一个片段?

于 2012-07-28T16:00:25.263 回答