0

所以无论我读过什么,即使我做对了,我似乎也无法掌握异步和等待的窍门。例如,我的创业公司就有这个。

启动.js

  await CommandBus.GetInstance();
  await Consumers.GetInstance();

调试跳转到 CommandBus 的 get 实例的末尾(为 rabbitmq 启动一个通道)并启动 Consumers.GetInstance(),但由于通道为空而失败。 命令总线.js

export default class CommandBus {
  private static instance: CommandBus;
  private channel: any;
  private conn: Connection;

  private constructor() {
    this.init();
  }

  private async init() {
    //Create connection to rabbitmq
    console.log("Starting connection to rabbit.");
    this.conn = await connect({
      protocol: "amqp",
      hostname: settings.RabbitIP,
      port: settings.RabbitPort,
      username: settings.RabbitUser,
      password: settings.RabbitPwd,
      vhost: "/"
    });

    console.log("connecting channel.");
    this.channel = await this.conn.createChannel();
  }

  static async GetInstance(): Promise<CommandBus> {
    if (!CommandBus.instance) {
      CommandBus.instance = new CommandBus();
    }

    return CommandBus.instance;
  }
  public async AddConsumer(queue: Queues) {
    await this.channel.assertQueue(queue);
    this.channel.consume(queue, msg => {
      this.Handle(msg, queue);
    });
  }
}

消费者.js

export default class Consumers {
  private cb: CommandBus;
  private static instance: Consumers;

  private constructor() {
    this.init();
  }

  private async init() {
    this.cb = await CommandBus.GetInstance();
    await cb.AddConsumer(Queues.AuthResponseLogin);
  }

  static async GetInstance(): Promise<Consumers> {
    if (!Consumers.instance) {
      Consumers.instance = new Consumers();
    }

    return Consumers.instance;
  }
}

抱歉,我意识到这是在 Typescript 中,但我想这并不重要。调用 cb.AddConsumer 时会出现此问题,该命令可在 CommandBus.js 中找到。它尝试针对尚不存在的通道断言队列。看不懂的,在看。我觉得我已经覆盖了所有等待区域,因此它应该等待频道创建。CommandBus 始终作为单例获取。如果这会引起问题,我不会,但它也是我所涵盖的领域之一等待。任何帮助都非常感谢大家。

4

1 回答 1

0

您不能真正在构造函数中使用异步操作。问题是构造函数需要返回你的实例,所以它不能也返回一个承诺,当它完成时会告诉调用者。

所以,在你的Consumers课堂上,await new Consumers();是没有做任何有用的事情。 new Consumers()返回一个Consumers对象的新实例,因此当您await认为它实际上并没有等待任何东西时。记住对await你有用的事情是await一个承诺。它没有任何特殊的权力来等待你的构造函数完成。

解决这个问题的常用方法是创建一个工厂函数(在您的设计中可以是静态的),它返回一个解析为新对象的承诺。

由于您还尝试制作单例,因此您将在第一次创建承诺时缓存它,并始终将承诺返回给调用者,以便调用者始终使用它.then()来获取完成的实例。他们第一次调用它时,他们会得到一个尚未完成的承诺,但后来他们会得到一个已经履行的承诺。在任何一种情况下,它们都只是.then()用来获取实例。

我不太了解 TypeScript,无法向您建议执行此操作的实际代码,但希望您能从描述中获得想法。变成GetInstance()一个工厂函数,它返回一个 Promise(你缓存的)并让这个 Promise 解析到你的实例。

像这样的东西:

static async GetInstance(): Promise<Consumers> {
  if (!Consumers.promise) {
      let obj = new Consumers();
      Consumers.promise = obj.init().then(() => obj);
  }
  return Consumers.promise;
}

然后,调用者会这样做:

Consumers.getInstance().then(consumer => {
    // code here to use the singleton consumer object
}).catch(err => {
    console.log("failed to get consumer object");
});

您必须在任何涉及初始化对象的异步操作(如 CommandBus)的类中执行相同的操作,并且每个.init()调用都需要调用基类super.init().then(...),因此基类也可以做它的事情来正确初始化,并承诺您的.init()也链接到基类。或者,如果您正在创建本身具有工厂函数的其他对象,那么您.init()需要调用这些工厂函数并将它们的 Promise 链接在一起,以便.init()返回的 Promise 也链接到其他工厂函数 Promise (所以您.init()返回的 Promise 将直到所有依赖对象都完成后才解决)。

于 2018-10-30T13:44:26.930 回答