1

我有以下代码:

loadOpportunities: function () {
    return $.getJSON("/Opportunity/GetAll").pipe(function (result) {
        //do stuff
    });
},

loadTypes: function () {
    return $.getJSON("/OpportunityTypes/GetAll", null, function (result) {
      //do stuff
    });
},

loadView: function () {
    var self = this;
    var deferred = $.Deferred();
    if (!self.viewLoaded) {
        imp2.mainViewModel.loading(true);
        deferred.pipe($.get('/Opportunities/Opportunity/GetView')
        .done(function (data) {
            $('div#opportunity').html(data);
        }));
        deferred.pipe(self.loadTypes);
        deferred.pipe(self.loadOpportunities)
        .done(function () {
            imp2.mainViewModel.loading(false);
        });
    }
    return deferred.resolve();
},

我想在 loadView 完全完成后做一些事情,包括所有回调。换句话说:我想在 GetView ajax 调用、loadTypes 函数和 loadOpportunities 函数(包括它们的回调)都完成时执行一些代码。

当未全部完成时,会触发以下 then 回调函数。我怎样才能做到这一点?

self.loadView.then(function () {
    //Do something after ALL stuff, including all callbacks is done in loadView
});
4

1 回答 1

1

我觉得在这里使用延迟、承诺和管道可能会有些混乱。

我认为你想要的是这样的(http://jsfiddle.net/JohnMunsch/ghdgD/):

var testObject = {
  loadOpportunities: function () {
    // This is basically saying that we're getting something via AJAX, but we want to take a swipe at changing it
    // before anyone else gets it, so by using pipe we're returning a different promise than the AJAX promise and
    // the value returned from that is what we want others to get.
    return $.ajax({
        url: "/echo/json/",
        data: opportunities,
        type: "POST"
      }).pipe(
        function (result) {
          console.log("[1]", result);

          // Change the result in some way and then return the changed result. Otherwise there's no point in using
          // a pipe() here, a done() would work as well.
          result.opportunities[0].name = "meat packer";

          return result;
        }
      );      
  },

  loadTypes: function () {
    // This example is different from loadOpportunities in that it isn't using a pipe() so what is returned from
    // this function is the same promise that $.ajax() gave us. Any call to .done() just echos back the same
    // promise you gave it.
    return $.ajax({
        url : "/echo/json/",
        data : opportunityTypes,
        type : "POST"
      }).done(
        function (result) {
          console.log("[1]", result);

          // We can do something with the data received here, but outside of this function, anything that tied to
          // the promise from the $.ajax() will see the original data, not any changes we might make here.                    
        }
      );
  },

  loadView: function () {
    // The self = this, scope = this, etc. that you see at the start of functions is only necessary if there are
    // closures below where this might be something else. This code doesn't have that so this line is unneeded.
    //
    // Be careful about cargo-cult programming and just doing something because you do it everywhere else or you
    // see someone else do something. Try to understand why it's there.
    // var self = this;

    if (!this.viewLoaded) {
      console.log("loading");
      var viewPromise = $.ajax({
          url : "/echo/json/",
          data : view,
          type : "POST"
      }).done(
        function (result) {
          console.log("[1]", result);

          // We might take this and dump it in a <div> somewhere.
        }
      );

      var typesPromise = this.loadTypes();
      var opportunitiesPromise = this.loadOpportunities();

      // The following line hands back a single promise (that's what $.when() does) that wraps all three of the
      // other promises and only fires when all three have completed OR when any one of them has failed.
      return $.when(typesPromise, opportunitiesPromise, viewPromise).done(
        function () {
          // Here I've tied a function to the promise as soon as I've created it. But since I'm handing back the
          // promise, I can also tie functions to it later as well, even after the promise has resolved or been
          // rejected.
          console.log("[2]", "All done loading types, views, and opportunities");
        }
      );
    }

    return true;
  }
};

// Note: Unlike with all the steps labeled [1], I don't believe there is a formal ordering for the
// .done()'s attached to the same promise like both [2] and [3]. So it may be possible for those two steps to
// happen [2]/[3] or [3]/[2] although they always happened [2]/[3] in my testing.
$.when(testObject.loadView()).done(
  function () {
    console.log("[3]", "This will only trigger after all the steps in load view have completed.");
  }
);

Note: I changed the style of your AJAX calls but that was just because I needed to build a working example in jsFiddle. Click on the link above to see this in action over there. It works and it orders things as you expect. You probably don't need pipe as much as you think unless you plan to manipulate the AJAX results before anyone tied to the promise sees the results. Otherwise, .done(), .fail(), and .when() will handle most situations.

于 2012-04-13T14:57:32.067 回答