如何在 Ionic 3 中执行多个异步操作并在一切完成后关闭加载器?

Posted

技术标签:

【中文标题】如何在 Ionic 3 中执行多个异步操作并在一切完成后关闭加载器?【英文标题】:How to perform multiple asynchronous actions in Ionic 3 and dismiss a loader after everything is done? 【发布时间】:2018-05-09 10:26:22 【问题描述】:

我开发了一个 Ionic 3 应用程序,它可以选择按需缓存文章内容列表。我正在使用Storage,它使用承诺进行操作。

我的代码如下:

article-service.ts

getArticleFullData(articleId: number) 
    let appSettings = this.appSettingsService.getSettings();
    let params = this.utilsService.serializeQueryParams(appSettings);
    let url = `$this.apiBasePathArticle/GetArticleFullData?articleId=$articleId&$params`;
    this.loggingService.logInfo("Getting article full data from url " + url); 
    return this.http.get(url)
        .map(res => res.json());

article-cache-service.ts

// saves a single article to the cache
private saveToCache(articleId: number): Observable<any> 

    let key = articleId;
    let obs = this.articleService.getArticleFullData(key);
    obs.subscribe(data => 
        this.storage.set(this.GetArticleFullKey(key), data as ArticleFullData);
    ,
        err => 
            this.loggingService.logError("Failed to cache data for article: " + key);
        
    );

    return obs;


// tries to cache a list of article in the cache
// avoids overwriting the data, if exists
public saveArticleDataToCache(articles: ArticleBriefData[]): Observable<[]> 

    var obsArray = [];
    for (let article of articles) 
        let key = this.GetArticleFullKey(article.ArticleId);
        this.storage.get(key)
            .then(data => 
                console.log("Storage data for article " + key, data);

                if (!data) 
                    obsArray.push(this.saveToCache(article.ArticleId));
                
            )
            .catch(err => 
                this.loggingService.logError("Failed to check storage for key " + key, err);
            );
    

    var ret = Observable.forkJoin(obsArray);
    return ret;

包含加载器的代码

    this.loadingCtrl.create( content: "Caching data. Please wait..." );
    this.loadingCtrl.present();

    var all = this.articleCacheService.saveArticleDataToCache(this.articles);
    var subscr = all
        .toPromise()
        .then(data => 
            console.log("All data: ", data);
            this.loadingCtrl.dismiss();
            this.loggingService.logInfo("Successfully cached articles ");
        )
        .catch(err => 
          this.loadingCtrl.dismiss();
          this.loggingService.logError("Failed to cache data: ", JSON.stringify(err));
        
    );

缓存过程正确执行,但then代码立即执行(dataundefined

我也尝试过subscribe 的方法,但是subscribe 中的代码似乎没有被执行(虽然缓存被正确执行了):

    var all = this.articleCacheService.saveArticleDataToCache(this.articles);
    var subscr = all
        .subscribe(data => 
            console.log("All data: ", data);

            this.loadingCtrl.dismiss();
            this.loggingService.logInfo("Successfully cached articles ");
        ,
        err => 
          this.loadingCtrl.dismiss();
          this.loggingService.logError("Failed to cache data: ", JSON.stringify(err));
        
    );

我显然无法在这里与Observables 正常合作。

问题:如何在 Ionic 3 中执行多个异步操作并在所有操作完成后关闭加载器?

【问题讨论】:

【参考方案1】:

您可以使用forkjoin轻松做到这一点。

注意:我已从我的应用程序中提取代码。因此请根据您的用例进行更改。如果您使用的是RXJS 5.5.2,请更改imports根据最新changes here

import  Observable  from "rxjs/Observable";
import 'rxjs/add/observable/forkJoin'

let myTopicApi = this.getMytopic();
let myarticlApi = this.getMyArticles();
let myPicksAPi = this.getMyPicks();
Observable.forkJoin([myTopicApi, myarticlApi, myPicksAPi])
  .subscribe(res => 
    this.arrangeMytopics(res[0]);
    this.arrangeMyArticles(res[1]);
    this.arrangeMyPicks(res[2]);
    if (loading)  loading.dismiss(); loading = null; //here you can dismiss your loader
  ,
  error =>  if (loading)  loading.dismiss(); loading = null; //here you can dismiss your loader ,
  () =>  );

getMytopic() 
    return this.topicSer.getMyTopics().map((res: any) => res.json()).map((res: any) => res = res.categories)
      .catch((err: any) =>   )
  

【讨论】:

感谢您的快速回答。我正在使用rxjs 5.4.0。我的代码已经在使用forkJoin,但它没有按预期工作。这可能与 getArticleFullData 正在做的事情有关,但我无法弄清楚出了什么问题(我也添加了该函数的代码)。 试试let 而不是var 看看。 而且您似乎没有正确使用forkjoin。我已经更新了我的帖子。希望您能更好地了解如何使用forkjoin 我意识到我做错了什么。我用于forkJoinobsArray 是在this.storage.get(key)then 中构造的。我已将代码简化为将“saveToCache”(返回Observable)推送到数组中,并且工作正常。但是,我丢失了缓存中存在的检查(我不明白为什么存储也不允许同步方法),但如果我不知道如何解决它,这是另一个问题的问题。 是的,问题是您尝试同时使用observablepromise。但是您不能那样使用。您必须将observable 转换为promise 或将promise 转换为observable。然后该问题也将消失。【参考方案2】:

Sampath 的回答帮助我找到了问题所在。我做错的是我构造Observable数组的方式(它在forkJoin中使用):在存储get返回的Promisethen函数内推入列表。

修复此问题后我的代码还包括检查项目是否存在(以避免 HTTP 调用和重置存储数据):

article-service.ts

getArticleFullData(articleId: number) 
    let appSettings = this.appSettingsService.getSettings();
    let params = this.utilsService.serializeQueryParams(appSettings);
    let url = `$this.apiBasePathArticle/GetArticleFullData?articleId=$articleId&$params`;
    return this.http.get(url)
        .map(res => res.json());

article-cache-service.ts

我正在使用flatMap 链接Observable 通过检查存储是否已经具有密钥和实际获取。如果数据存在,我会返回一个空的Observable 以实际取消该过程。

private saveToCache(articleId: number): Observable<any> 

    let key = this.GetArticleFullKey(articleId);

    let storageObs = Observable.fromPromise(this.storage.get(key));
    let getArticleObs = this.articleService.getArticleFullData(articleId);

    let obs = storageObs.flatMap(data => 
        if (!data)
            return getArticleObs;
        else
            return Observable.of(null);
    );

    obs.subscribe(data => 

        if (data)
          this.storage.set(key, data as ArticleFullData);
    ,
        err => 
            this.loggingService.logError("Failed to cache data for article: " + articleId);
        
    );

    return obs;


public saveArticleDataToCache(articles: ArticleBriefData[]): Observable<[]> 

    let obsArray = [];
    for (let article of articles) 

        let obs = this.saveToCache(article.ArticleId);
        obsArray.push(obs);
    

    let ret = Observable.forkJoin(obsArray);
    return ret;

实际订阅

onCacheRefresh() 
    // articles are not loaded for some reason
    if (!this.articles)
        return;

    this.loadingCtrl.create( content: "Caching data. Please wait..." );
    this.loadingCtrl.present();

    let all = this.articleCacheService.saveArticleDataToCache(this.articles);
    let subscr = all
        .subscribe(data => 
            console.log("All data: ", data);

            this.loadingCtrl.dismiss();
            this.loggingService.logInfo("Successfully cached articles ");
        ,
        err => 
          this.loadingCtrl.dismiss();
          this.loggingService.logError("Failed to cache data: ", JSON.stringify(err));
        
    );

【讨论】:

以上是关于如何在 Ionic 3 中执行多个异步操作并在一切完成后关闭加载器?的主要内容,如果未能解决你的问题,请参考以下文章

ionic3 异步请求中.then的作用,以及如何理解JavaScript Promise

如何运行多个异步函数然后执行回调?

如何在angularjs执行多个异步操作后再执行指定操作

Python 异步IO

如何使 ASP.NET Core Web API 操作异步执行?

从 C# 异步运行多个 PowerShell 脚本