FWIW There is a plan to move from async to sync with a nice migration path, and it’s much easier than you might think.
It starts with understanding that the data structure we return from both sync and async relationship access is too flat. Relationships need information about meta/errors/links beyond just the records that they contain. Today this information is difficult to access generally and especially hard to access within a template.
Were we to introduce a better primitive that was slightly less flat we could use the opportunity to also change how you access that data structure both in your JS code and in your templates.
For example, imagine relationships had the following interface for their resolved data
interface Relationship {
data: Identifier|Identifier[]|null;
links?: Links;
errors?: Error[];
meta?: object
}
and to access the records for a relationship in your JS
let data = await fetchRelationshipData(record.myRelationship);
and to access those same records in your template with an async boundary in the template
{{#let (fetch-relationship-data record.myRelationship) as |list|}}
{{#each list as |item|}}
...
{{/each}}
{{/let}}
Or to use the available errors
{{#each record.myRelationship.errors as |error|}}
{{/each}}
Or to just output a count or some other meta info
Showing 1 - {{record.myRelationship.data.length}}
of {{record.myRelationship.meta.total}}.
<button onclick={{loadLink record.myRelationship.links.next}}>
Load More
</button>
You would opt into this by changing from belongsTo
and hasMany
to resource
and collection
decorators on your classes (or by using a custom record class).
This would allow for incremental migration, and the functional patterns would be made to work with both to make that even easier.