类中奇怪的异步/等待行为

Posted

技术标签:

【中文标题】类中奇怪的异步/等待行为【英文标题】:Strange async/await behaviour in a class 【发布时间】:2017-09-13 02:12:57 【问题描述】:

节点 7.9.0

这是场景:

class TestClass 
  constructor() 
    const x = await this.asyncFunc()
    console.log(x)
  
  async asyncFunc() 
    return new Promise((accept) => 
      setTimeout(() => accept("done"), 1000)
    )
  

new TestClass()

第3行的错误是Unexpected token this

好的,所以我就这样结束了...const x = await (this.asyncFunc())

现在我得到await is not defined

这让我觉得节点 7.9.0 不知道等待/同步,尽管 http://node.green/ 告诉我它受支持并且它的示例运行得很好。

(function()
(async function ()
  await Promise.resolve();
  var a1 = await new Promise(function(resolve)  setTimeout(resolve,800,"foo"); );
  var a2 = await new Promise(function(resolve)  setTimeout(resolve,800,"bar"); );
  if (a1 + a2 === "foobar") 
    console.log("passed")
  
());
)()

【问题讨论】:

请注意,在示例中,awaitasync 函数中使用。你的代码在这方面是不同的。 ...所以你需要一个异步构造函数... 见this question。 你不能在构造函数中使用await。但是,您可以使用this.asyncFunc().then(console.log)。但是,通常应避免在构造函数中进行异步处理。 您可能想签出this。 【参考方案1】:

恕我直言,在构造函数中使用 await 是不明智的。构造函数应该是轻量级的。构造函数不应失败。

MSDN about constructors

考虑提供简单的、理想情况下的默认构造函数。一个简单的 构造函数的参数数量很少,所有参数 是原始类型或枚举。 考虑使用静态工厂 方法而不是构造函数,如果需要的语义 操作不直接映射到新实例的构造, 或者如果遵循构造函数设计指南感觉不自然。

一个好的解决方案是创建一个静态异步工厂方法,该方法将调用一个简单的私有构造函数并调用您的私有 asyncfunc。

这样,人们可以通过调用您的静态异步工厂方法来创建您的类的实例的唯一方法,因此您可以确定您的 asyncFunc 已被调用。

好消息是:这一切都没有诡计

我对JAVA不是很熟悉,下面是C#,但我想你会明白的

class TestClass

    public async Task<TestClass> CreateAsync()
    
        TestClass instance = new TestClass(); // lightWeight
        Promise p = await instance.AsyncFunc();
        // do what you need to do with your Promise
        Console.Log(...)
        return instance;
    

    // private constructor: only CreateAsync can call it:
    private TestClass()
    
        // make it lightweight
    

    public async Task<Promise> AsyncFunc()
    
        return new Promise((accept) => 
        
            setTimeout(() => accept("done"), 1000)
        );
    

用法。

而不是

TestClass myObject = new TestClass();

你会这样做:

 TestClass myObject = await TestClass.Creat();

您的调用将是异步的,构造函数是轻量级的,并且您确定您的 asyncFunc 已被调用并等待。

【讨论】:

【参考方案2】:

添加异步启动功能并使用它来解决问题:

所以最初的例子可以用异步启动函数来修复:

class TestClass 
  constructor() 
    this.start()
  
  async start() 
    const x = await this.asyncFunc1()
    console.log(x)
  
  async asyncFunc1() 
    return new Promise((accept) => 
      setTimeout(() => accept("done"), 1000)
    )
  

new TestClass()

如果你想在 asyncFunc1 中进一步调用 await,你也需要让 promise 函数异步...

class TestClass 
  constructor() 
    this.start()
  
  async start() 
    const x = await this.asyncFunc1()
    console.log(x)
  
  async asyncFunc1() 
    return new Promise(async (accept) => 
      const x = await this.asyncFunc2()
      accept(x)
    )
  
  async asyncFunc2() 
    return new Promise((accept) => 
      accept("done")
    )
  

new TestClass()

【讨论】:

事情是对象被构造并且它的引用返回给调用者而无需等待启动函数运行。例如,调用者可能开始使用其他尚未准备好的成员函数。最好让构造函数初始化对象(即使用默认值或传入值)并让调用者自己调用'start(..)'。

以上是关于类中奇怪的异步/等待行为的主要内容,如果未能解决你的问题,请参考以下文章

列表理解中奇怪的 lambda 行为

UIImageView 中奇怪的对齐行为

Chrome中奇怪的document.cookie行为[重复]

C中奇怪的malloc行为

Firefox 中奇怪的引导进度条行为

Typescript 中奇怪的类型推断行为