How can I abort a DS.Store#query?

I’m fixing a bug in some code that – unfortunately – uses the store in a component:

store: Ember.inject.service(),
this.get('store').query('foo'...).then((data) => this.set('data', data))

Now, if the promise resolves after the component has been destroyed, we get an error about calling set on a destroyed object. So far, I’ve tried:

this.get(‘store’).query…then((data) => if (this.isDestroyed || this.isDestroying) { return }…

which works but I think it’s much better for the component to clean up after itself in the willDestroyElement hook.

So, how can I note the xhr request from this.get('store').query.. so that I can abort it in willDestroyElement?

Thanks!

Look at ember-concurrency, which introduces a “tasks” concept which is like a sequence of promises which can be canceled, and scopes these tasks to a parent object (for instance, a component), canceling them automatically when the parent is destroyed. The sequence of promises is actually generated from an ES6 generator, so in your task, instead of writing

promise.then(result => {
  // block of code
})

you write:

var result = yield promise;
// block of code

If the task is canceled while the promise is out, the yield line throws an exception, and the block of code is skipped, which is effectively like your manual check for this.isDestroyed || this.isDestroying.

This doesn’t actually abort the XHR request, but I don’t think you need to do that here. Aborting requests usually doesn’t actually save your server any work, since it doesn’t know the request has been aborted.

If you’re worried about sending data over the wire when you don’t need to, and really want to abort the request, I have solved this same problem before I knew about ember-concurrency by making the component inject an object into the adapter, which causes the adapter to store the request in that object, basically handing it to the component. It’s a pretty poor solution, but you might be able to make it a bit better by using a service instead: make the adapters issue AJAX requests through a service, and the components can talk to the service to cancel requests.

2 Likes

Thanks @kellen. ember-concurrency looks like a great solution.