mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-27 19:38:13 +08:00
Add fetch concurrency limit on obs annotation loading (#1318)
* add fetch concurrency limit * add tests for PromiseLimit
This commit is contained in:
+14
-12
@@ -7,6 +7,7 @@ import {
|
||||
doBinaryRequest,
|
||||
dispatchNetworkErrorMessageToUser
|
||||
} from "../util/actionHelpers";
|
||||
import { PromiseLimit } from "../util/promiseLimit";
|
||||
import { requestReembed, reembedResetWorldToUniverse } from "./reembed";
|
||||
|
||||
/*
|
||||
@@ -15,28 +16,29 @@ we don't need.
|
||||
*/
|
||||
function obsAnnotationFetchAndLoad(dispatch, schema) {
|
||||
const obsAnnotations = schema?.schema?.annotations?.obs ?? {};
|
||||
const columns = obsAnnotations.columns ?? [];
|
||||
const index = obsAnnotations.index ?? false;
|
||||
const columns = (obsAnnotations.columns ?? []).filter(
|
||||
col => col.name !== index
|
||||
);
|
||||
|
||||
const plimit = new PromiseLimit(4);
|
||||
return Promise.all(
|
||||
columns
|
||||
.filter(col => col.name !== index)
|
||||
.map(col => {
|
||||
columns.map(col =>
|
||||
plimit.add(() => {
|
||||
const path = `annotations/obs?annotation-name=${encodeURIComponent(
|
||||
col.name
|
||||
)}`;
|
||||
const url = `${globals.API.prefix}${globals.API.version}${path}`;
|
||||
return doBinaryRequest(url);
|
||||
})
|
||||
.map(rqst => rqst.then(buffer => Universe.matrixFBSToDataframe(buffer)))
|
||||
.map(resp =>
|
||||
resp.then(df =>
|
||||
return doBinaryRequest(url).then(buffer => {
|
||||
const df = Universe.matrixFBSToDataframe(buffer);
|
||||
dispatch({
|
||||
type: "universe: column load success",
|
||||
dim: "obsAnnotations",
|
||||
dataframe: df
|
||||
})
|
||||
)
|
||||
)
|
||||
});
|
||||
});
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
This class provides a means to resolve Promises asynchronously,
|
||||
and limit the number concurrently executed. For example, if
|
||||
you want to involve a large number of API endpoints using fetch(),
|
||||
but want to limit the number simultaneously outstanding.
|
||||
|
||||
Example usage:
|
||||
|
||||
const plimit = new PromiseLimit(2);
|
||||
return Promise.all([
|
||||
plimit.add(() => fetch('/foo')),
|
||||
plimit.add(() => fetch('/bar')),
|
||||
plimit.add(() => fetch('/baz'))
|
||||
])
|
||||
*/
|
||||
export class PromiseLimit {
|
||||
constructor(maxConcurrency) {
|
||||
this.queue = new Set();
|
||||
this.maxConcurrency = maxConcurrency;
|
||||
this.pending = 0;
|
||||
}
|
||||
|
||||
add(fn, ...args) {
|
||||
// fn - must return a promise
|
||||
// args - will be passed to fn
|
||||
return new Promise((resolve, reject) => {
|
||||
this.queue.add({ fn, args, resolve, reject });
|
||||
this._resolveNext(false);
|
||||
});
|
||||
}
|
||||
|
||||
_resolveNext = (completed = true) => {
|
||||
if (completed) this.pending -= 1;
|
||||
|
||||
while (this.queue.size > 0 && this.pending < this.maxConcurrency) {
|
||||
const task = this.queue.values().next().value; // order of insertion
|
||||
this.pending += 1;
|
||||
this.queue.delete(task);
|
||||
const { resolve, reject, fn, args } = task;
|
||||
|
||||
try {
|
||||
const result = fn(...args);
|
||||
result.then(this._resolveNext, this._resolveNext);
|
||||
result.then(resolve, reject);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user