1

I am processing a bunch of records and producing an array of hashes for a third party library. For the life of me I can't figure out why this doesn't work.

export default Route.extend({
  model: function(params) {
   let qp = {viewName: 'byDay'};

  return this.store.query('job-receipt', qp).then(
   (receipts)=>
    all(
      receipts.map(
        (receipt)=>
          hash({
            stockCode: receipt.get('job')
                                   .then(job => job.get('stockCode'))
                                   .then(stockCode => stockCode.get('stockCode')),
            productClass: receipt.get('job')
                                  .then(job => job.get('stockCode'))
                                  .then(stockCode => stockCode.get('productClass'))
                                  .then(productClass => productClass.get('descr')),
            qtyRecvd: receipt.get('qtyRecvd')
          })
        )
      )
);

If I keep reentering the route, eventually the promises resolve. If I check, the productClass promise just straight up gets called with a null value. Why isn't it waiting for the stockCode.get('productClass') to resolve? I know there are real values in there because it does eventually resolve.

I'm missing something super basic. I've tried Ember.get(thing, keypath) etc. Don't these all return promises? Isn't the RSVP.hash supposed to wait for all the promises to resolve before proceeding? Like I said, I know the data is good because eventually it does resolve (as opposed to me just not handling a rejection).

EDIT:

I changed the productClass promise to this:

productClass: receipt.get('job')
        .then(job => job.get('stockCode'))
        .then(stockCode => stockCode.get('productClass'))
        .then(productClass => {if (!productClass) {return 'foo';} return productClass.get('descr');})

Now the report renders correctly every time albeit with nonsense. If I navigate to another route, and then back to this route, it renders perfectly. So, that makes it hard for me to believe I have some kind of data error. And even some of the stock codes return the right product class - not 'foo' - on the first run through. I'm not even sure how to debug this further.

edit

Just saw this. May be a bug after all.

[3.2.0+] Snapshot’s related data has become null #5565

NullVoxPopuli
  • 61,906
  • 73
  • 206
  • 352
  • What is retval? But it looks good. You're absolutely sure your responses are correct? – Lux Aug 16 '18 at 02:40
  • ur right. that was an artifact of me trying to clean it up for posting. retval is supposed to be receipts. I sure think they're right! I'll edit my posting and put some more info – John Larson Aug 16 '18 at 02:56

2 Answers2

2

It turns out it is a bug. The bug for belongs-to.js causes the model to not wait for internalModel to complete loading before resolving the promise. The fix linked below resolves the issue

[BUGFIX] use internalModel promise if already loading #5562

1

I think the main issue is that "reciepts.job" is likely a DS.belongsTo relationship right? If you switch it to load job: DS.belongsTo('job', {async: false}), that will force ember-data to load that property synchronously (and will save a lot of headaches). But that requires the data to be available either in the json response.

If that doesn't work, you should investigate ember concurrency. Use that to clean up your code to look a little more straightforward. You'll have to fill in some blanks or change things where I misunderstood your use case, but this is a likely a good starting point.

The idea is to continue to break all the asynchronous calls into seperate tasks. Each ember-concurrency task object returns a promise, so you can keep bundling them up until you get to model which you can return just like any other promise.

//model.js
import {task} from 'ember-concurrency';
.....

model() {
  return this.get('loadData').perform();
},
loadData: task(function*(){
  let reciepts = yield this.store.query('job-receipt', qp);
  let promises = reciepts.map(r => {
    return this.get('loadNestedData').perform(r);
  })
  return all(promises)
}),
loadNestedData: task(function*(reciept) {
  let job = yield receipt.get('job');
  return hash({
    stockCode: job.get('sockcode')
  });
})
  • yes, job is a belongsTo relationship. So are stockCode and productClass. That being said, the code as I wrote it *should* work though, right? I really don't want to have to work around what I'm starting to think is a bug if it's really a bug. I'd rather submit it as a bug. I really don't understand why the promise is "resolving" and passing a null value to the next "then" when the value truly isn't null. – John Larson Aug 16 '18 at 15:31
  • I don't think it's actually a bug in ember code. I suspect there's some sort of bug in your code that's not handling the promise correctly. I suspect one of those results in a DS.PromiseObject and is causing some problems (which is why the easiest fix is to make the relationships {async: false} – Donald Wasserman Aug 16 '18 at 20:50
  • No, it’s definitely bug 5562 I referenced in my answer. When I use the patch in that issue it works fine. In fact it worked fine when I downgraded to 2.16. – John Larson Aug 16 '18 at 21:12