角度:检测昂贵同步功能内部的变化



我的功能可以执行昂贵的同步任务。就我而言,这是通过PDFKIT生成客户端的PDF,但让我们在循环睡眠中仿真。

我想在运行任务之前显示一个"加载"旋转器,并在完成任务后将其隐藏。

如果一切都同步运行,那么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 run() {
  this.running = true;
  await this.sleep(2000);
  this.running = false;
}

直到函数结束并且没有显示任何内容。

才能检测到更改。

我想问题是JavaScript仍然是单线线程,并且在创建时仍会立即运行承诺,因此一切基本上仍然同步运行。

,即使使用theckEtectorRef.detectchanges((强迫变更检测无济于事。

到目前为止,我发现的唯一解决方案是在Settimeout Hack中运行它:

setTimeoutRun() {
  this.running = true;
  setTimeout(() => {
    this.sleep(2000);
    this.running = false;
  }, 100);
}

,但看起来不像正确的正式解决方案。

settimeout真的是唯一的方法吗?

plunker:https://embed.plnkr.co/ywsihulpmqjqwzamxvjn/

如果您的作业同步,则加载逻辑也需要同步。除了利用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();
        });
    }
}

好吧,你走了。使用可观察到的解决方案。Timer((应该解决您的问题。计时器等待2秒钟,然后设置为false。

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();
        });
    }
}

最新更新