0

I have a complicated object graph I'm building up in an Ember Controller.

export default Container({
  username: DS.attr('string'),
  items: DS.hasMany('item')
})

export default SomeDetail({
  foo: DS.attr('string')
})

export default Item({
  detail_type: DS.attr('string'),
  detail_id: DS.attr('number'),
  container: DS.belongsTo('container')
})

So, to set all this up, I'm basically trying to

  1. Create the conatainer,
  2. Then, create the details, of which there may be many
  3. Then, create the items, of which there will be as many of the details
  4. Wait for all promises to resolve
  5. Fire off a custom rest action to "activate" the container once it has all of it's stuff.

The code looks like this (coffee), simplified but I think the gist is there

promises = []
store = @store
items = @get('itemsInMyController')
store.createRecord('container',
  username: @get('username')
).save().then(container) ->
  items.forEach (item) ->
    store.createRecord('detail',
      # Set Properties
    ).save().then (detail) ->
      item = store.createRecord('item',
        # Set Properties
      )
      promsies.push item
      item.save()

Ember.RSVP.allSettled(promsies).then (responses) ->
  # Perform Activate Action

When all the promises resolve, everything is how I want it, however, allSettled is firing way too soon, because it's reached before the details have resolved, so the items haven't been created, so there's nothing in the array. This also happens if I add the details to the array, because it's still reached well before the items have been created.

The only thing I can thing of is to have separate arrays tracking the different promises, and having a nested allSettled as each one resolves, but this is starting to feel pretty hairy, and I'm wondering if there's a better way.

Thanks!

Kingpin2k
  • 47,277
  • 10
  • 78
  • 96
DVG
  • 17,392
  • 7
  • 61
  • 88
  • Not really familiar with coffee script, but is the allSettled statement inside or outside the container creation's promise? Nvm see answer from @Bergi – jcbvm Jun 01 '15 at 20:16

1 Answers1

3

You need to return promises from your then callbacks so that you can properly unnest them. So first return the item promise from that callback and get a promise that you can actually push to your promises array immediately in that loop:

promises = []
@get('itemsInMyController').forEach (item) =>
  promise = @get('store').createRecord('detail',
    # Set Properties
  ).save().then (detail) =>
    item = @get('store').createRecord('item',
      # Set Properties
    )
    item.save() # this returns a promise
  ) # and `promise` resolves with that result eventually
  promises.push promise

Now you got an array of promises that you can actually pass to allSettled. You must not call that outside of the then callback for the container as well (because promises would still be empty by then), but inside the callback, and you can again return that promise for the array so that you flatten your chain.

And I'd recommend not to use forEeach and manually build up that array, just use map:

@store.createRecord('container',
  username: @get('username')
).save().then (container) =>
  promises = @get('itemsInMyController').map (item) =>
    @get('store').createRecord('detail',
      # Set Properties
    ).save().then (detail) =>
      @get('store').createRecord('item',
        # Set Properties
      ).save()
  Ember.RSVP.allSettled promises
.then (responses) ->
  # Perform Activate Action
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • @Kingpin2k: thanks for the hint through the edit! I guess however it's more idiomatic CS to just use fat arrows. And I'm trying to as few things in the original code as possible :-) – Bergi Jun 01 '15 at 22:36
  • Yeah, I assumed it was a fat arrow thing, but not being familiar with CS (other than CS<->JS sites), I figured I'd avoid trying to act like I knew what I was doing with them, for fear of screwing it up ;) – Kingpin2k Jun 01 '15 at 22:39
  • I spent hours today trying to figure this out. The nested promises weren't getting resolved until after the template was rendered. Returning an array using RSVP.all() was the key. – Caltor Sep 14 '20 at 14:51