等待变量设置的有效方法?

时间:2018-06-20 11:27:30

标签: javascript angular typescript asynchronous

我正在寻找一种有效的方法来检查Angular应用程序中是否已设置了变量,因此我不必继续检查其是否已设置。当用户首次请求页面时,我有一项将所有产品加载到变量中的服务。示例:

export class ProductService {
    public products: Product[];

    constructor(private http: HttpClient) { }

    init(): any {
        if (!this.products) {
            this.http.get<Product[]>("https://my-api.com/GetAllProducts").subscribe(products => {
                this.products = products;
            });
        }
    }
}

然后我在productService.init()文件中简单地说app.component.ts来称呼它,以确保它初始化产品以备后用。但是,我也有一种方法可以从产品ID中获取产品:

getProductFromProductId(productId: string) {
    return this.products.find(x => x.id === productId);
}

getProductFromProductId方法在每个页面上至少调用一次。假设init()方法需要2000毫秒的加载时间,因为有很多产品,调用getProductFromProductId()会失败,因为尚未设置products。要解决此问题,我可以执行以下操作:

getProductFromProductId(productId: string) {
    if(!this.products) {
        setTimeout(() => {
            console.log("Products not loaded yet");
            getProductFromProductId(productId);
        }, 10);
    } else {
        return this.products.find(x => x.id === productId);
    }
}

上面的代码检查是否每10毫秒设置一次products。如果没有,它只会再次调用该方法。

但是,让我们想象一下该服务中有10种方法。这有很多“ 尚未加载”的可能性,这意味着我将不得不重复相同的代码10次(或将其移至函数中,但您明白了)。最大的问题是getProductFromProductIdinit都被同时调用,因此,基本上我们需要等待init完成。

有更好的方法吗?

2 个答案:

答案 0 :(得分:0)

您可以将产品列表更改为“可观察”。我建议一个BehaviorSubject。

import {BehaviorSubject, Observable} from 'rxjs';

export class ProductService {
    public products: BehaviorSubject<Product[]> = new BehaviorSubject([]);

    constructor(private http: HttpClient) { }

    init(): any {
        if (!this.products) {
            this.http.get<Product[]>("https://my-api.com/GetAllProducts").subscribe(products => {

                if(products){
                    this.products.next(products);
                }

            });
        }
    }

    getProducts(): Observable<Product[]> {
        return this.products.asObservable();
    }

}

然后让您的ts文件预订BehaviorSubject。一旦满足,所有订户都会得到通知,可以做出反应。

TS文件

ngOnInit(): void {
    this.productService.getProducts().subscribe( list => {

        if(list) {
            // fill your local list
            this.productList = list;

            // or call a method that processes this list
            this.processProductList(list);

            // or other actions
        }

    });
}

答案 1 :(得分:0)

由于我无法使用BehaviorSubject(不知道为什么,但我根本无法使用它),所以我选择了常规的Subject。这是一个示例:

product.service.ts

@Injectable()
export class ProductService {
    public products = new Array<Product>();
    public productsSubject: Subject<Product[]> = new Subject();

    constructor(private http: HttpClient) { }

    init(): any {
        this.http.get<Product[]>("https://my-api.com/GetAllProducts").subscribe(products => {
            this.products = products;
            this.productsSubject.next(products);
        });
    }

    getProductByProductId(productId: string): Observable<Product> {
        if (this.products.length > 0)
            return of(this.products.find(x => x.id === productId));

        return new Observable(observer => {
            this.productsSubject
                .subscribe(products => {
                    var p = products.find(x => x.id === productId);
                    observer.next(p);
                    observer.complete();
                });
        });
    }
}

如果要使用import { of } from 'rxjs';,请记住在顶部添加of()

然后在我们的组件中,我们可以简单地做到:

random.component.ts

this.productsService.getProductByProductId(productId)
    .subscribe(product => {
        //do something with product
    });

这确保我们的服务方法相对较小并且易于理解,而组件的代码也较小且易于理解。