When does record relationship getting return promises?

Say there is a parent model:

// models/order.js    
export default Model.extend({
    products: DS.hasMany('order-product')

and a child model:

// models/order-product.js 
export default Model.extend({
    order: DS.belongsTo('order')

Ember documentation says relationships are async by default. So, I can get products next way

order.get('products').then(products => ...);

But I’ve loaded all data in the route while getting an order. So, if I’m trying to get products synchronously (like const products = order.get('products');) everything works fine. I suppose ember resolves array immediately if records have been loaded into the store.

Am I right? Where can I find official explanations about that?


The documentation treats it as a Promise, and it is one. If you always treat is as a Promise you won’t go wrong. However, it’s also true that the value you get back acts like both a Promise and an Array. This is why you can use the value directly in a handlebars template and the template will update correctly when the promise resolves (regular promises don’t do that). If the data’s not available yet, the array will be empty, and then its contents will update when the promise resolves.

This works great in templates, but it can be pretty confusing in javascript. That is why there are newer methods for synchronously interrogating relationships to see whether they’re loaded and get their values if they are available. See the hasMany and belongTo methods on Model.

thanks a lot for your answer. But anyway, how does emberjs dicide if it will be a promise or an array?

For example, I get a parent model with it’s relationships in the route, pass it to a component, and want to use that relationships inside init hook. Can I be sure I can use it synchronously and it’s not needed to make init hook as async method?

It’s always both.

My recommendation is to use it synchronously via the hasMany or belongsTo methods. That makes it clear that your component really needs synchronously available data, and you could even make the component throw if the data is not already loaded. This protects you from getting hard-to-fine bugs if a later refactor causes your component to be used in a new way.