Promises and computed properties

I have a very similar situation where I have a route that displays a filtered array based on a query param.

App.Location = DS.Model.extend({
   ...
   sites: DS.hasMany('site', { async: true })
   ...
});

App.Site = DS.Model.extend({
   ...
   siteCategory: DS.belongsTo('site-category', { async: true })
   ...
});

App.SiteCategory = DS.Model.extend({
   ...
   name: DS.attr('string')
   ...
});

My route returns back all related sites for a location then each tab sets the query param to a different siteCategory name to filter the data. This works fine when clicking from one tab to another but not on the initial load due to unresolved promises:

queryParams: ['siteCategory'],
siteCategory: null,

sortProperties: [ 'isActive', 'name' ],
sortedContent: Ember.computed.sort('model', 'sortProperties'),
filteredContent: Ember.computed('siteCategory', 'model.@each.isDeletedFromApplication', function () {
    var self = this;
    var sites = this.get('sortedContent');
    var siteCategory = this.get('siteCategory');

    return sites.filter(function (site) {
        // logs UNDEFINED on page load
        // logs the proper value when the queryParam changes
        console.log(site.get('siteCategory'),get('name');
        return site.get('siteCategory').get('name') === siteCategory && 
               !site.get('isDeletedFromApplication');
    });
})

Would love to know how to work around the unresolved promises in filteredContent on first load.

The approach described by ef2 above should work here. I used it like so: https://gist.github.com/manmal/4a9836922e11157d7ecf (actual working example).

I haven’t written Ember code for a while now, but what I think could work in your example is to create a big, fat promise for all that stuff that you currently have inside the function() {...}, and then use it like so (using promisedProperty.coffee from my gist):

filteredContent: promisedProperty(someDefaultValue, -> 
  # insertYourBigFatPromiseDefHere
).property('sortedContent', 'siteCategory')
1 Like

I’ve been toying with this issue lately as well…Particularly the case where I want to show feedback of the promise’s state (loading/error). I hacked together this {{render-with-promise}} helper as a proof of concept of what IMO is gold. Basically just removes the state checking boilerplate and gives you loading/error psuedo-substates for free in the same fashion as route-backed promises do, but unlike routes you can render multiple promise-based resources on the same page at once since you don’t need to maintain URL state.

Most often, this is handy for dashboard/widget style views where you are loading many different calls individually and don’t want to halt the page transition (as RSVP.hash() would).

Feedback welcome.

2 Likes

Are these issues between promises and computed properties are gonna be fixed? (next release of Ember data) I would like to use Ember data in one of my projects but I’m concern about this… It’s actually pretty hard to find a work around. There’s few month I read that the DataBoundPromise were supposed to be used inside computed properties. What about it ?

Would love to know if someone has some work around or feedback.

Would love to see some examples for this as well. I have a firebase backed project full of async relationships.

What I have been doing is adding a _resolve property to my models, which build new properties based on relevant promise-returning properties. (Convention – for a promise property _foo I have a property foo which is undefined when promise unresolved and then set to the resolution when it resolves.)

I get _resolve in root afterModel … this works, but involves some unfortunate coupling & boilerplate. One problem is that I need to call _resolve on all the models used – so root needs to know what is used (See Mapping queryParams -- proposal or request for best practice).

Currently, using the router seems to be the “best practice” for resolving promises… but the mapping from routes to application state isn’t perfect.

Isn’t this a replication of the PromiseObjectMixin ?

1 Like

I could have been using PromiseObjectMixin (wasn’t aware of it). Either way the coupling problem remains as routes can’t be specified in enough precision to capture application state, and they need to know what to resolve whatever the method.

This does the right thing and I think will help me work around some hacky code in our app but Ember is complaining that I can’t call set because this has already been destroyed:

Assertion failed: calling set on destroyed object

The promise is a Q promise not Ember.RSVP.Promise if that makes a difference. Any ideas how to work around this?

Thanks!

That error means that your object got destroyed while the promise was still resolving. You can defend against that by checking this.isDestroyed before calling set.

1 Like

Has there been any movement on this, or is the workaround from @ef4 still the way to address it? FWIW the workaround was effective for me.

The scenario I have is creating tagString property on a post resource/model that represents a composed string of the tags associated with a given post. No matter how structure/sequence things making use of return or .then() I end up with a promise as the result. Seems like a reasonable scenario that deserves some mechanism within the framework to deal with.

My modestly informed 2 cents…

The way I solve this currently is for components that have a LOT of run loop calls or promises I add them to a little async manager which ensure that on willDestroy they are all torn down or cancelled appropriately.

@ef4’s suggestion is actually the solution to the problem and not a “work-around.” This is something I managed to overlook for a solid year of coding in Ember but you need to clean up anything that can happen later in willDestroy(). This includes promises as well as anything that you’ve put off with a debounce or runLater call. This has solved a lot of weirdness in our specs where we had to wrap a lot of code in Ember.run. It’s also solved most of weird, intermittent spec failures we were having.

1 Like

Yeah, you can’t avoid dealing with asynchrony.

However, I do think there are still opportunities to make the framework handle more cases more declaratively.

1 Like

Take a look at GitHub - bengillies/ember-parallel: An Ember.js plugin proving integration with Parallel.js.

It’s the correct answer, using computed properties to handle promises and waiting to resolved before display in template is wrong, because template render shouldn’t blocked by a promise.(if still in pending), instead once the promised fulfilled, the template should be refresh.

That’s the Ember Way™

2 Likes

This was perfect i created a service and just works out of the box

import Ember from ‘ember’;

export default Ember.Service.extend({ promiseProxy(promise) { let ObjectPromiseProxy = Ember.ObjectProxy.extend(Ember.PromiseProxyMixin); return ObjectPromiseProxy.create({ promise: promise }); }, });

Ember Data has some utilities for this purpose: DS.PromiseObject and DS.PromiseArray. These are ObjectProxy mixed with PromiseProxyMixin

1 Like

While refreshing content when a promise is resolved is a useful pattern, Ember routing relies on waiting for promises to resolve before rendering – I don’t think there is one single “Ember way” here.

In many cases, for example when navigating the object graph (say post.comments) in our components, we are forced to deal with async outside of the route hooks.

There was a lot of discussion around this. For those interested, here is an article I wrote not long ago with best practices for dealing with promises in computed properties: The Guide to Promises in Computed Properties - Ember Igniter

1 Like