【问题标题】:Asynchronous Code not working in expected order in TS异步代码在 TS 中未按预期顺序工作
【发布时间】:2018-01-11 22:46:57
【问题描述】:

尝试使用 Provider 从使用 4 级嵌套回调函数的 firebase 服务器获取数据。这是我的提供者的样子。

import { Injectable } from '@angular/core';

declare var window;

@Injectable()
export class SettingsProvider {

  constructor() {

  }

  public async getFireBaseRemoteConfig(): Promise<any> {
    if (window["FirebasePlugin"]) {
      await window["FirebasePlugin"].fetch(600, async result => {
        // activate the fetched remote config
        console.log(JSON.stringify(result)); //Always "OK"
        await window["FirebasePlugin"].activateFetched( async (bool) => {

          await this.getSliderImageURLs().then((d) => {
            console.log("d", d);
            return d;
          });
        });
      })
    }
  }

  public async getSliderImageURLs(): Promise<any> {

    var urls = [];

    await window["FirebasePlugin"].getValue("slider_images", result => {
      urls = JSON.parse(result);
      console.log("FROM getSliderImageURLs()");
      console.log(urls)
      return Promise.resolve(urls);
    })
  }
}

我希望执行顺序是 getFireBaseRemoteConfig 函数仅在 getSliderImageURLs 函数获取数据并返回时才返回。

根据文档,功能应按以下顺序执行:

fetch -> activateFetched -> getValue

在我的页面中,我是这样使用它的。

console.log(this.settingsProvider.getFireBaseRemoteConfig()); // Just for testing

我做错了什么?非常感谢任何帮助。

编辑:添加控制台日志

OK
d undefined
FROM getSliderImageURLs()
["","","",""] //this is my URLs array

【问题讨论】:

  • 您的代码有几个问题。请查看异步等待使用情况。一方面,您没有在getSliderImageUrl 中返回任何内容,getValue 是否返回承诺?
  • 感谢您的评论。不,getValue 不返回承诺。我需要返回 getValue 返回的值(来自函数)。
  • 可以添加控制台日志吗?
  • @Ben 刚刚添加了控制台日志。顶部的函数不等待底部的函数。它在底部函数返回我需要的实际数据之前完成执行。

标签: typescript asynchronous dependency-injection async-await ionic3


【解决方案1】:

您似乎错误地同时使用了传统的 Promise 结构和 async/await。您可以或使用 Async/await(下面的答案 1,但是您必须将回调转换为 Promise),或者您可以使用传统的 Promise(答案 2),或者使用新的 ionic 本机 firebase 插件(答案 3),它将转换回调对你的承诺。

  1. 异步/等待结构

当你使用 async await 时,代码会像同步一样执行,所以不需要在 async await 中使用 .then()。你的代码会变成这样。

  public async getFireBaseRemoteConfig(): Promise<any> {
        if (window["FirebasePlugin"]) {
          let remoteConfig = await window["FirebasePlugin"].fetch(600);
          let sliderImageUrls = await this.getSliderImageURLs();
          // From here you can do whatever you want with remoteConfig & sliderImageUrls, as they are resolved with async/await
          return sliderImageUrls;
    }

    public async getSliderImageURLs(): Promise<any> {
        let sliderImagesJson = await window["FirebasePlugin"].getValue("slider_images")
        let sliderImages = JSON.parse(sliderImagesJson);
          return Promise.resolve(sliderImages);
        })
      }

这种 await/async 方法只有在链式 Promise 的情况下才是首选,在你的情况下链式回调。因此,要使这个答案起作用,您需要将回调更改为这样的承诺:

//Convert the fetch Remote Config callback to a promise
  public getRemoteConfig(cacheExpirationSeconds:number=600):Promise<any> {
    return new Promise<boolean>((resolve, reject) => {
      window["FirebasePlugin"].fetch(cacheExpirationSeconds,(result) => resolve(result))
    });
  }

在您的示例中,我建议使用更传统的承诺结构,如下所示:

  1. 传统的 Promise 结构(没有 async/await)

     public getFireBaseRemoteConfig(): Promise<any> {
    return new Promise<any>((resolve, reject) => {
      if (window["FirebasePlugin"]) {
        window["FirebasePlugin"].fetch(600, (result) => {
          // activate the fetched remote config
          console.log(JSON.stringify(result)); //Always "OK"
          window["FirebasePlugin"].activateFetched((bool) => {
            this.getSliderImageURLs().then((d) => {
              console.log("d", d);
              resolve(d);
            });
          });
        })
      }
      else {
        reject();
      }
    });
    

    }

// getSlider 函数:

 public getSliderImageURLs(): Promise<any> {
    return new Promise<any>((resolve, reject) => {
      var urls = [];
      window["FirebasePlugin"].getValue("slider_images", (result) => {
        urls = JSON.parse(result);
        console.log("FROM getSliderImageURLs()");
        console.log(urls)
        resolve(urls);
      });
    });
  }
  1. 使用离子原生firebase

使用新的(目前处于测试阶段)firebase 原生插件,它已经为您将回调转换为承诺,因此您可以使用答案 1。

编辑:阐明传统 Promises 与同步/等待之间区别的示例

传统promise结构中的一个例子:

getPrice(currency: string): Promise<number> {
  return this.http.get(this.currentPriceUrl).toPromise()
    .then(response => response.json().bpi[currency].rate);
}

和这个 async/away 一样:

async getPrice(currency: string): Promise<number> {
  const response = await this.http.get(this.currentPriceUrl).toPromise();
  return response.json().bpi[currency].rate;
}

example的来源

希望对你有帮助

【讨论】:

  • 所以这意味着执行then函数(有时带有成功回调)和使用await是一回事。那正确吗?我们可以用两种不同的方式处理同一个对象,对吧?
  • 是的,我会添加一个例子来澄清。
  • getValue() 是一个接受两个参数的函数,第二个是回调。我们也可以使用 await 吗?我对此不确定,但一定会尝试并回帖。感谢您的努力。
  • 不,如果您使用回调,则不能,但如果您重写 getValue 以返回一个承诺,就像您在 getSliderImageURLs 上所做的那样,那么您可以使用我推荐的 async/await。
  • 哦,我做不到。 getValue() 是 Firebase SDK 的一部分。它可以正常工作。我不能重写它。 fetchactivateFetched 函数也是如此。所以这行不通。
猜你喜欢
  • 2018-03-02
  • 1970-01-01
  • 1970-01-01
  • 2018-05-29
  • 2019-04-19
  • 1970-01-01
  • 1970-01-01
  • 2020-03-04
  • 2019-04-07
相关资源
最近更新 更多