-
Notifications
You must be signed in to change notification settings - Fork 36
/
Copy pathResultSlicer.ts
76 lines (62 loc) · 2.5 KB
/
ResultSlicer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import IClientContext from '../contracts/IClientContext';
import IResultsProvider, { ResultsProviderFetchNextOptions } from './IResultsProvider';
export interface ResultSlicerFetchNextOptions extends ResultsProviderFetchNextOptions {
// Setting this to `true` will disable slicer, and it will return unprocessed chunks
// from underlying results provider
disableBuffering?: boolean;
}
export default class ResultSlicer<T> implements IResultsProvider<Array<T>> {
private readonly context: IClientContext;
private readonly source: IResultsProvider<Array<T>>;
private remainingResults: Array<T> = [];
constructor(context: IClientContext, source: IResultsProvider<Array<T>>) {
this.context = context;
this.source = source;
}
public async hasMore(): Promise<boolean> {
if (this.remainingResults.length > 0) {
return true;
}
return this.source.hasMore();
}
public async fetchNext(options: ResultSlicerFetchNextOptions): Promise<Array<T>> {
// If we're asked to not use buffer - first try to return whatever we have in buffer.
// If buffer is empty - just proxy the call to underlying results provider
if (options.disableBuffering) {
if (this.remainingResults.length > 0) {
const result = this.remainingResults;
this.remainingResults = [];
return result;
}
return this.source.fetchNext(options);
}
const result: Array<Array<T>> = [];
let resultsCount = 0;
// First, use remaining items from the previous fetch
if (this.remainingResults.length > 0) {
result.push(this.remainingResults);
resultsCount += this.remainingResults.length;
this.remainingResults = [];
}
// Fetch items from source results provider until we reach a requested count
while (resultsCount < options.limit) {
// eslint-disable-next-line no-await-in-loop
const hasMore = await this.source.hasMore();
if (!hasMore) {
break;
}
// eslint-disable-next-line no-await-in-loop
const chunk = await this.source.fetchNext(options);
result.push(chunk);
resultsCount += chunk.length;
}
// If we collected more results than requested, slice the excess items and store them for the next time
if (resultsCount > options.limit) {
const lastChunk = result.pop() ?? [];
const neededCount = options.limit - (resultsCount - lastChunk.length);
result.push(lastChunk.splice(0, neededCount));
this.remainingResults = lastChunk;
}
return result.flat();
}
}