我有一个执行昂贵的同步任务的功能。 在我的例子中,它是通过pdfkit生成的客户端pdf,但让我们通过while循环睡眠模拟它。
我想在运行任务之前显示一个“加载”微调器,并在完成任务时隐藏它。
如果一切都是同步运行的,Angular将无法在一切结束之前运行更改检测循环,所以我想我必须找到一种异步运行它的方法。
我尝试将它包装在一个承诺中,所以让我说我有:
sleep(ms) {
return new Promise((resolve, reject) => {
const expire = (new Date()).getTime() + ms;
while ((new Date()).getTime() < expire);
resolve();
});
}
但是我是否使用.then()调用运行它:
run() {
this.running = true;
this.sleep(2000).then(() => {
this.running = false;
});
}
或使用async / await:
async run() {
this.running = true;
await this.sleep(2000);
this.running = false;
}
在功能结束之前未检测到更改,并且不会显示任何内容。
我想问题是Javascript仍然是单线程的,并且承诺在创建时仍然会立即运行,所以一切基本上仍然是同步运行。
但即使使用ChangeDetectorRef.detectChanges()强制进行更改检测也无济于事。
我到目前为止找到的唯一解决方案是在setTimeout hack中运行它:
setTimeoutRun() {
this.running = true;
setTimeout(() => {
this.sleep(2000);
this.running = false;
}, 100);
}
但它看起来不像正确的正式解决方案。
setTimeout真的是唯一的方法吗?
答案 0 :(得分:0)
如果您的工作是同步的,那么您的加载逻辑也需要同步。除了利用setTimeout
的事件循环之外,没有别的办法(据我所知)。
换句话说,您不能执行this.loading = true
之类的操作,因为必须等待更改检测才能运行。你必须明确地启动加载逻辑(手动将加载器元素添加到DOM,以便它立即可见,等等)。
否则,根据定义,它必须等到你的长同步作业完成后再开始加载,因为加载器逻辑本身是异步的,因此只有在当前执行(即同步作业)完成后才会被调用
例如:
@Component({...})
export class MyComponent implements OnInit {
constructor(private loadingService: LoadingService) {}
ngOnInit() {
// Start synchronous loader...
this.loadingService.start();
// By the time code reaches here, loader should be visible.
// Do expensive synchronous task...
this.expensiveSynchronousTask().then(() => {
// Stop synchronous loader...
this.loadingService.stop();
});
}
}
答案 1 :(得分:0)
好的,你走了。这个带有Observable.timer()的解决方案应该可以解决您的问题。计时器等待2秒并设置运行然后为假。
import { Component, NgModule, OnInit, OnDestroy } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Observable, Subscription } from 'rxjs/Rx';
@Component({
selector: 'my-app',
template: `
<button (click)="run()">Run</button>
<div *ngIf="running">Running...</div>
`
})
export class App implements OnInit, OnDestroy {
running = false;
private timer = Observable.timer(2000);
private subscription: Subscription;
constructor() { }
ngOnInit() { }
ngOnDestroy() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
run() {
this.running = true;
this.subscription = this.timer.subscribe(() => {
this.running = false;
this.subscription.unsubscribe();
});
}
}