如何保证最短运行时间
How to ensure minimum operation duration
我正在使用动态数据(基于 this example)构建 Angular Material mat-tree
。
当用户展开一个节点时,会显示一个进度条,同时从服务器检索子节点列表。
基本代码如下所示:
toggleNode(node: DynamicFlatNode, expand: boolean) {
if (expand) {
// Shows progress bar
node.isLoading = true;
this
.database
.getChildren(node.id)
.subscribe(
values => {
// Create and add the descendant nodes
...
},
error => { console.error(error); },
() => {
// Hides progress bar
node.isLoading = false;
});
} else {
// Remove descendant nodes
...
}
}
问题是如果数据检索太快,进度条只是闪烁。
我想添加一个延迟,以便进度条至少显示 250 毫秒。
我希望可见时间为 min(250ms, data retrieving time)
而不是 250 ms + data retrieving time
。
我目前的解决方案是使用虚拟承诺,它在数据检索之前开始并在 complete
回调中等待:
toggleNode(node: DynamicFlatNode, expand: boolean) {
if (expand) {
// Shows progress bar
node.isLoading = true;
const delay = new Promise(resolve => setTimeout(resolve, 250));
this
.database
.getChildren(node.id)
.subscribe(
values => {
// Create and add the descendant nodes
...
},
error => { console.error(error); },
async () => {
await delay;
// Hides progress bar
node.isLoading = false;
});
} else {
// Remove descendant nodes
...
}
}
有更简洁的方法吗?
您可以利用 RxJS timer
和 forkJoin
来引入人为延迟。我还建议使用 finalize
运算符来设置 isLoading
标志。如果不是,您必须在订阅的 error
和 complete
回调中设置它。
重要提示:forkJoin
只会在所有源可观察对象完成时发出。所以你需要确保你的 observable this.database.getChildren(node.id)
完成。如果没有,您可以使用 take(1)
强制它在第一次发射后完成,假设您不需要发射流。
尝试以下方法
import { forkJoin, timer } from 'rxjs';
import { finalize, map, take } from 'rxjs/operators';
const DELAY = 250;
toggleNode(node: DynamicFlatNode, expand: boolean) {
if (expand) {
node.isLoading = true; // <-- show progress bar
forkJoin({
timer: timer(DELAY),
children: this.database.getChildren(node.id),
}).pipe(
map(({ children }) => children), // <-- ignore the `timer` emission
finalize(() => (node.isLoading = false)) // <-- hide progress bar
).subscribe({
next: (values: any) => {
// create and add the descendant nodes
},
error: (error: any) => {
// handle error
},
});
} else {
// remove descendant nodes
}
}
工作示例:Stackblitz。使用 DELAY
变量来观察引起的延迟。
您可以从 here
获取想法
import { of, map, Observable, combineLatest, delay, timer } from 'rxjs';
import { mapTo, startWith, tap } from 'rxjs/operators';
const GET_DATA_DELAY = 100;
const PROGRESS_DELAY = 250;
const getData = (): Observable<any> => {
return of('data').pipe(delay(GET_DATA_DELAY));
};
const log = {
next: (value: any) => console.log(`Date:${ (new Date()).toISOString()} : `, value),
error: (err: any) => console.error(err),
};
const delay$ = timer(PROGRESS_DELAY).pipe(mapTo('timer'),tap(log));
const data$ = getData().pipe(tap(log));
const isLoading$ = combineLatest([delay$, data$]).pipe(
mapTo(false),
startWith(true),
map(value => `IsLoading: ${value}`),
);
isLoading$.subscribe(log);
// Open the console in the bottom right to see results.
我正在使用动态数据(基于 this example)构建 Angular Material mat-tree
。
当用户展开一个节点时,会显示一个进度条,同时从服务器检索子节点列表。
基本代码如下所示:
toggleNode(node: DynamicFlatNode, expand: boolean) {
if (expand) {
// Shows progress bar
node.isLoading = true;
this
.database
.getChildren(node.id)
.subscribe(
values => {
// Create and add the descendant nodes
...
},
error => { console.error(error); },
() => {
// Hides progress bar
node.isLoading = false;
});
} else {
// Remove descendant nodes
...
}
}
问题是如果数据检索太快,进度条只是闪烁。
我想添加一个延迟,以便进度条至少显示 250 毫秒。
我希望可见时间为 min(250ms, data retrieving time)
而不是 250 ms + data retrieving time
。
我目前的解决方案是使用虚拟承诺,它在数据检索之前开始并在 complete
回调中等待:
toggleNode(node: DynamicFlatNode, expand: boolean) {
if (expand) {
// Shows progress bar
node.isLoading = true;
const delay = new Promise(resolve => setTimeout(resolve, 250));
this
.database
.getChildren(node.id)
.subscribe(
values => {
// Create and add the descendant nodes
...
},
error => { console.error(error); },
async () => {
await delay;
// Hides progress bar
node.isLoading = false;
});
} else {
// Remove descendant nodes
...
}
}
有更简洁的方法吗?
您可以利用 RxJS timer
和 forkJoin
来引入人为延迟。我还建议使用 finalize
运算符来设置 isLoading
标志。如果不是,您必须在订阅的 error
和 complete
回调中设置它。
重要提示:forkJoin
只会在所有源可观察对象完成时发出。所以你需要确保你的 observable this.database.getChildren(node.id)
完成。如果没有,您可以使用 take(1)
强制它在第一次发射后完成,假设您不需要发射流。
尝试以下方法
import { forkJoin, timer } from 'rxjs';
import { finalize, map, take } from 'rxjs/operators';
const DELAY = 250;
toggleNode(node: DynamicFlatNode, expand: boolean) {
if (expand) {
node.isLoading = true; // <-- show progress bar
forkJoin({
timer: timer(DELAY),
children: this.database.getChildren(node.id),
}).pipe(
map(({ children }) => children), // <-- ignore the `timer` emission
finalize(() => (node.isLoading = false)) // <-- hide progress bar
).subscribe({
next: (values: any) => {
// create and add the descendant nodes
},
error: (error: any) => {
// handle error
},
});
} else {
// remove descendant nodes
}
}
工作示例:Stackblitz。使用 DELAY
变量来观察引起的延迟。
您可以从 here
获取想法import { of, map, Observable, combineLatest, delay, timer } from 'rxjs';
import { mapTo, startWith, tap } from 'rxjs/operators';
const GET_DATA_DELAY = 100;
const PROGRESS_DELAY = 250;
const getData = (): Observable<any> => {
return of('data').pipe(delay(GET_DATA_DELAY));
};
const log = {
next: (value: any) => console.log(`Date:${ (new Date()).toISOString()} : `, value),
error: (err: any) => console.error(err),
};
const delay$ = timer(PROGRESS_DELAY).pipe(mapTo('timer'),tap(log));
const data$ = getData().pipe(tap(log));
const isLoading$ = combineLatest([delay$, data$]).pipe(
mapTo(false),
startWith(true),
map(value => `IsLoading: ${value}`),
);
isLoading$.subscribe(log);
// Open the console in the bottom right to see results.