Asynchronous Actions

I’ve been thinking about something over the past week, and I wonder if this would solve the animation probem, and maybe a number of others as well. My thinking is to build on the conventions developed in the async model work and make actions support similar asyncronicity using promises.

Use Case

I have a paginated list of data and I want to have a sliding transition between pages. However, at the moment pages are not tied to routes, they are just in the controller. When I click the button for the next page, I’d like to do the following:

  1. Slide the current page out to the left
  2. Load the second page of data
  3. Slide the next page in from the right

I think this could be accomplished by having before, after and action callbacks for each action. If any of the callbacks return a promise, the entire process will block until that promise resolves. So the next page action would like like:

Ember.Component.extend({
  actions: {
    nextPage: {
      before: function() {
        return new Ember.RSVP.Promise(function(resolve, reject) {
          this.$("#main-data").addClass("slide-out-left");
          this.$("#main-data").on("webkitTransitionEnd", resolve);
        });
      },
      action: function() {
        this.loadNextPage();
      },
      after: function() {
        return new Ember.RSVP.Promise(function(resolve, reject) {
          this.$("#main-data").addClass("slide-out-left");
          this.$("#main-data").on("webkitTransitionEnd", resolve);
        });
      }
    }
  }
});

Of course, if I specify the action normally without the callback style it would work synchronously just like it does today. This would keep it backwards compatible with the current action helper, but give the functionality to anyone who needs it.

I’m interested in working on this some more, but wanted to see what others think of this API. Would this be useful for other cases besides animation? Are there any upcoming changes to the action helper that might conflict with this?

2 Likes

I think this would be incredibly helpful. Thinking beyond animation, I can see this being useful for analytics; tracking page changes on success, tracking user actions, performing debug actions.

I am glad to see you surfacing the need to expand actions; we need to respond to the before/during/after steps, as you have outlined, and maybe even failure cases that get triggered on the bubble-up of an exception.

@jason_a_corns I think it would be particularly useful to have components that do animations in before/after callbacks but bubble up to controllers for the actual action.

I have an initial concept working at http://jsbin.com/EyivaCE/2/edit. It doesn’t take bubbling into a account yet, but does pause the action until the promise resolves. This would allow for doing any kind of animation, or setting analytics/tracking data, or anything else you may want to do prior to an action running.

My next step is to handle bubbling as I mentioned above. For example, you could have a component that fades elements in and out, but get loaded by a controller. Or that slides elements out, loads new data, then slides the new data in.

One question I have is: should the action callback use promises to pause the “after” callback? In the use case of pagination, we don’t need the promise because paginating is handled synchronously by the controller. There are cases where I could see doing an ajax request without going through a route, and in those cases maybe that would be useful (for example, showing/hiding a spinner on a type-ahead lookup).

@joefiorini, which was the community feedback?

I find this feature really useful so far it can be used in multiple different scenarios. I already reported it at “Blocking actions and user events until promise resolution”.

Regarding your specific case, are the before/after filters really necessary?