Skip to content

Commit

Permalink
Replace the adapter cancellation methods with a cancellation token sy…
Browse files Browse the repository at this point in the history
…stem
  • Loading branch information
DingoEatingFuzz committed May 17, 2019
1 parent cbe88fa commit 913888f
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 133 deletions.
98 changes: 20 additions & 78 deletions ui/app/adapters/watchable.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { get, computed } from '@ember/object';
import { get } from '@ember/object';
import { assign } from '@ember/polyfills';
import { inject as service } from '@ember/service';
import queryString from 'query-string';
Expand All @@ -9,52 +9,24 @@ export default ApplicationAdapter.extend({
watchList: service(),
store: service(),

xhrs: computed(function() {
return {
list: {},
track(key, xhr) {
if (this.list[key]) {
this.list[key].push(xhr);
} else {
this.list[key] = [xhr];
}
},
cancel(key) {
while (this.list[key] && this.list[key].length) {
this.remove(key, this.list[key][0]);
}
},
remove(key, xhr) {
if (this.list[key]) {
xhr.abort();
this.list[key].removeObject(xhr);
}
},
};
}),

ajaxOptions() {
ajaxOptions(url, type, options) {
const ajaxOptions = this._super(...arguments);
const key = this.xhrKey(...arguments);

const previousBeforeSend = ajaxOptions.beforeSend;
ajaxOptions.beforeSend = function(jqXHR) {
if (previousBeforeSend) {
previousBeforeSend(...arguments);
}
this.xhrs.track(key, jqXHR);
jqXHR.always(() => {
this.xhrs.remove(key, jqXHR);
});
};
const cancellationToken = (options || {}).cancellationToken;
if (cancellationToken) {
delete options.cancellationToken;

const previousBeforeSend = ajaxOptions.beforeSend;
ajaxOptions.beforeSend = function(jqXHR) {
cancellationToken.capture(jqXHR);
if (previousBeforeSend) {
previousBeforeSend(...arguments);
}
};
}

return ajaxOptions;
},

xhrKey(url, method /* options */) {
return `${method} ${url}`;
},

findAll(store, type, sinceToken, snapshotRecordArray, additionalParams = {}) {
const params = assign(this.buildQuery(), additionalParams);
const url = this.urlForFindAll(type.modelName);
Expand All @@ -63,7 +35,9 @@ export default ApplicationAdapter.extend({
params.index = this.watchList.getIndexFor(url);
}

const cancellationToken = get(snapshotRecordArray || {}, 'adapterOptions.cancellationToken');
return this.ajax(url, 'GET', {
cancellationToken,
data: params,
});
},
Expand All @@ -76,7 +50,9 @@ export default ApplicationAdapter.extend({
params.index = this.watchList.getIndexFor(url);
}

const cancellationToken = get(snapshot || {}, 'adapterOptions.cancellationToken');
return this.ajax(url, 'GET', {
cancellationToken,
data: params,
}).catch(error => {
if (error instanceof AbortError) {
Expand All @@ -86,7 +62,7 @@ export default ApplicationAdapter.extend({
});
},

reloadRelationship(model, relationshipName, watch = false) {
reloadRelationship(model, relationshipName, watch = false, cancellationToken) {
const relationship = model.relationshipFor(relationshipName);
if (relationship.kind !== 'belongsTo' && relationship.kind !== 'hasMany') {
throw new Error(
Expand All @@ -110,6 +86,7 @@ export default ApplicationAdapter.extend({
}

return this.ajax(url, 'GET', {
cancellationToken,
data: params,
}).then(
json => {
Expand Down Expand Up @@ -143,39 +120,4 @@ export default ApplicationAdapter.extend({

return this._super(...arguments);
},

cancelFindRecord(modelName, id) {
if (!modelName || id == null) {
return;
}
const url = this.urlForFindRecord(id, modelName);
this.xhrs.cancel(`GET ${url}`);
},

cancelFindAll(modelName) {
if (!modelName) {
return;
}
let url = this.urlForFindAll(modelName);
const params = queryString.stringify(this.buildQuery());
if (params) {
url = `${url}?${params}`;
}
this.xhrs.cancel(`GET ${url}`);
},

cancelReloadRelationship(model, relationshipName) {
if (!model || !relationshipName) {
return;
}
const relationship = model.relationshipFor(relationshipName);
if (relationship.kind !== 'belongsTo' && relationship.kind !== 'hasMany') {
throw new Error(
`${relationship.key} must be a belongsTo or hasMany, instead it was ${relationship.kind}`
);
} else {
const url = model[relationship.kind](relationship.key).link();
this.xhrs.cancel(`GET ${url}`);
}
},
});
15 changes: 15 additions & 0 deletions ui/app/utils/classes/xhr-token.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export default class XHRToken {
get xhr() {
return this._xhr;
}

capture(xhr) {
this._xhr = xhr;
}

abort() {
if (this.xhr) {
this.xhr.abort();
}
}
}
25 changes: 13 additions & 12 deletions ui/app/utils/properties/watch.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { get } from '@ember/object';
import RSVP from 'rsvp';
import { task } from 'ember-concurrency';
import wait from 'nomad-ui/utils/wait';
import XHRToken from 'nomad-ui/utils/classes/xhr-token';
import config from 'nomad-ui/config/environment';

const isEnabled = config.APP.blockingQueries !== false;

export function watchRecord(modelName) {
return task(function*(id, throttle = 2000) {
const token = new XHRToken();
if (typeof id === 'object') {
id = get(id, 'id');
}
Expand All @@ -17,59 +19,58 @@ export function watchRecord(modelName) {
yield RSVP.all([
this.store.findRecord(modelName, id, {
reload: true,
adapterOptions: { watch: true },
adapterOptions: { watch: true, cancellationToken: token },
}),
wait(throttle),
]);
} catch (e) {
yield e;
break;
} finally {
this.store
.adapterFor(modelName)
.cancelFindRecord(modelName, id);
token.abort();
}
}
}).drop();
}

export function watchRelationship(relationshipName) {
return task(function*(model, throttle = 2000) {
const token = new XHRToken();
while (isEnabled && !Ember.testing) {
try {
yield RSVP.all([
this.store
.adapterFor(model.constructor.modelName)
.reloadRelationship(model, relationshipName, true),
.reloadRelationship(model, relationshipName, true, token),
wait(throttle),
]);
} catch (e) {
yield e;
break;
} finally {
this.store
.adapterFor(model.constructor.modelName)
.cancelReloadRelationship(model, relationshipName);
token.abort();
}
}
}).drop();
}

export function watchAll(modelName) {
return task(function*(throttle = 2000) {
const token = new XHRToken();
while (isEnabled && !Ember.testing) {
try {
yield RSVP.all([
this.store.findAll(modelName, { reload: true, adapterOptions: { watch: true } }),
this.store.findAll(modelName, {
reload: true,
adapterOptions: { watch: true, cancellationToken: token },
}),
wait(throttle),
]);
} catch (e) {
yield e;
break;
} finally {
this.store
.adapterFor(modelName)
.cancelFindAll(modelName);
token.abort();
}
}
}).drop();
Expand Down
Loading

0 comments on commit 913888f

Please sign in to comment.