当美元.应用于延迟数组,在所有解析之前运行

$.when.apply on array of deferreds runs before all resolved

本文关键字:运行 应用于 美元 延迟 数组      更新时间:2023-09-26

尝试为某些类构建先决条件链。由于未知的递归深度,每次创建新课程时都会运行一个ajax调用。DFDS是一个用于存储延迟的全局数组。COURSES是课程的全局数组。

var DFDS = []; //define global DFDS
var course = function( options ) {
  ...
  var self = this;
  this.p = { //prereqs object
    a: [], // array for prereqs
    rgx: /([A-Z]'w+[' ])'w+/g, 
    parse: function() {
      if (self.prereqs == '') return true;
      self.prereqs.replace(this.rgx,function(m) {
        //search through prereq string (self.prereqs) and run ajax for each match
        var id = m.split(' ');
        var dfd2 = $.Deferred();
        DFDS.push(dfd2);
        $.getJSON('/ajax/ajaxPrereqs.php',{subj:id[0],crs:id[1]},function(d) {
          var d = d[0];
          self.p.a.push(new course({
            ... //create new course in self.p.a[]
          }));
          dfd2.resolve();
        });
      });
    }
  };
  ...
  //run parse function when created
  this.p.parse();
  return this;
}

我能够得到我所期望的结构与所有正确的课程加载到所有正确的self.p.a[]数组。

初始化顶级课程:

$.getJSON('/ajax/ajaxPrereqs.php',{subj:$('#subj').val()}, function(data) {
  $.each(data,function() {
    var d = this;
    COURSES.push(new course({
      s: d.subj,
      c: d.crs,
      t: d.titleshrt,
      crd: d.credits,
      chr: d.contacthrs,
      prereqs: d.prereqs
    }));
  });

  console.log(DFDS.length); //displays 10
  $.when.apply($, DFDS).then(function() {
    console.log('DFDS all done???');
    console.log(DFDS.length);
    $.each(DFDS,function() {console.log(this.state())})
    $.each(COURSES,function() {
      this.render();
    })
  });
});

问题:$.when.apply在所有延迟解决之前运行。我的控制台显示:

24
(10) resolved
(14) pending

我还尝试每5毫秒运行一次日志记录功能:

var interval = setInterval(function() {
  console.log('--- intv ---');
  var pending = 0;
  var resolved = 0;
  $.each(DFDS,function() {
    switch(this.state()) {
      case 'pending': pending++; break;
      case 'resolved': resolved++; break;
      default: console.log('NOT PENDING OR RESOLVED');
    }
  });
  console.log(pending+' pending');
  console.log(resolved+' resolved');
},5);

$.when.apply运行前的最后一个控制台条目是14 pending, 8 resolved.

完成后有92个ajax调用。所有92个返回良好的数据(没有错误/失败)。

我怎么能告诉它运行.then()后所有dfd在数组(不只是一个在数组时$.when.apply定义?)

大致思路如下。我在这里有几个目标。

  1. 去掉你正在累积承诺的全局变量
  2. 让所有嵌套的承诺都链接到它们的父承诺上,这样你就可以等待父承诺,一切都会正常工作。
  3. 链接承诺,我用.then()处理程序替换$.getJSON()中的完成回调,然后从.then()处理程序返回嵌入的承诺。这会自动将它们链接到父元素上,所以父元素本身不会被解析,直到嵌入的元素也被解析。而且,这对任意深度的嵌入式承诺都有效。
  4. 去掉在承诺已经存在时创建和解决新的差异的反模式。

在摆脱全局变量的过程中,我不得不从course构造函数中删除承诺的创建,因为它不能返回承诺。因此,我创建了一个course.init(),其中创建并返回了承诺。然后,我们可以在局部变量中累积承诺,而避免全局变量。

总的思路是:

var course = function( options ) {
  ...
  var self = this;
  this.p = { //prereqs object
    a: [], // array for prereqs
    rgx: /([A-Z]'w+[' ])'w+/g, 
    parse: function() {
      var promises = [];
      // check if we have any prereqs to process
      if (self.prereqs !== '') {
          self.prereqs.replace(this.rgx,function(m) {
            //search through prereq string (self.prereqs) and run ajax for each match
            var id = m.split(' ');
            promises.push($.getJSON('/ajax/ajaxPrereqs.php',{subj:id[0],crs:id[1]}).then(function(d) {
              var d = d[0];
              var c = new course({
                ... //create new course in self.p.a[]
              }));
              self.p.a.push(c);
              // chain all the new promises created by c.init() onto our master promise
              // by returning a new promise from the .then() handler
              return(c.init());
            }));        
          });
      }
      // return a master promise that is resolve when all the sub promises 
      // created here are all done
      return $.when.apply($, promises);
    }
  };
  // call this to run the initial parse
  // returns a single promise that is resolve when all the promises are done
  this.init = function() {
      return this.p.parse();
  };
  ...
  return this;
}
$.getJSON('/ajax/ajaxPrereqs.php',{subj:$('#subj').val()}, function(data) {
  var promises = [];
  var COURSES = [];
  $.each(data,function() {
    var d = this;
    var c = new course({
      s: d.subj,
      c: d.crs,
      t: d.titleshrt,
      crd: d.credits,
      chr: d.contacthrs,
      prereqs: d.prereqs
    });
    COURSES.push(c);
    promises.push(c.init());    
  });
  $.when.apply($, promises).then(function() {
    console.log('promises all done');
    console.log(promises.length);
    $.each(promises,function() {console.log(this.state())})
    $.each(COURSES,function() {
      this.render();
    })
  });
});

这个成功了。我感谢大家的评论。它帮助我意识到这个问题是$.when.apply$.when.apply被定义时只观察DFDS中的延迟。

我把这个函数放在课程构造函数.parse()$.getJSON回调中。

function checkDFDS() {
  var completed = 0;
  $.each(DFDS,function() {
    if (this.state()=='resolved') completed++;
  });
  if (completed == DFDS.length) {
    $.each(COURSES,function() {
      this.render();
    })
  }
}