1/* 2 * Copyright (c) 2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16interface ITimeProvider { 17 getCurrent: () => number; 18} 19 20class DefaultTimeProvider implements ITimeProvider { 21 getCurrent(): number { 22 return Date.now(); 23 } 24} 25 26class Prefetcher implements IPrefetcher { 27 private datasource: IDataSourcePrefetching | null = null; 28 private readonly prefetchRangeEvaluator: IPrefetchRangeEvaluator; 29 private readonly timeProvider: ITimeProvider; 30 31 constructor(prefetchRangeEvaluator: IPrefetchRangeEvaluator, timeProvider: ITimeProvider) { 32 this.prefetchRangeEvaluator = prefetchRangeEvaluator; 33 this.timeProvider = timeProvider; 34 } 35 36 setDataSource(ds: IDataSourcePrefetching): void { 37 this.datasource = ds; 38 this.prefetchRangeEvaluator.setTotal(ds.totalCount()); 39 } 40 41 visibleAreaChanged(minVisible: number, maxVisible: number): void { 42 if (!this.datasource) { 43 throw new Error('No data source'); 44 } 45 46 const oldRangeToPrefetch = this.prefetchRangeEvaluator.rangeToPrefetch; 47 this.prefetchRangeEvaluator.visibleRangeChanged(minVisible, maxVisible); 48 49 this.prefetchDifference(oldRangeToPrefetch); 50 this.cancelDifference(oldRangeToPrefetch); 51 } 52 53 private prefetchDifference(oldRange: IndexRange): void { 54 this.prefetchRangeEvaluator.rangeToPrefetch.subtract(oldRange).forEachIndex((index) => { 55 const prefetchStart = this.timeProvider.getCurrent(); 56 57 const prefetchedCallback = (): void => { 58 if (!this.prefetchRangeEvaluator.rangeToPrefetch.contains(index)) { 59 return; 60 } 61 62 const oldRangeToPrefetch = this.prefetchRangeEvaluator.rangeToPrefetch; 63 64 const prefetchDuration = this.timeProvider.getCurrent() - prefetchStart; 65 this.prefetchRangeEvaluator.itemPrefetched(index, prefetchDuration); 66 67 this.prefetchDifference(oldRangeToPrefetch); 68 }; 69 70 const prefetchResponse = this.datasource!.prefetch(index); 71 if (prefetchResponse instanceof Promise) { 72 prefetchResponse.then(prefetchedCallback); 73 } else { 74 prefetchedCallback(); 75 } 76 }); 77 } 78 79 private cancelDifference(oldRangeToPrefetch: IndexRange): void { 80 if (!this.datasource!.cancel || this.prefetchRangeEvaluator.rangeToPrefetch.length > oldRangeToPrefetch.length) { 81 return; 82 } 83 oldRangeToPrefetch.subtract(this.prefetchRangeEvaluator.rangeToPrefetch).forEachIndex((index) => { 84 this.datasource!.cancel!(index); 85 }); 86 } 87} 88