88

作为一种组织模式,ES6 类可以为异步代码提供什么。下面是一个 ES7 async/await 的例子,一个 ES6 类可以有一个异步方法,或者 ES7 中的构造函数吗?

我可不可以做:

class Foo {
    async constructor() {
        let res = await getHTML();
        this.res = res
    }
}

而且,如果不是,构造函数应该如何工作呢?

class Foo {
    constructor() {
        getHTML().then( function (res) {
            this.res = res
        }
    }
}

如果这些模式都不起作用,ES6 中的构造函数(以及类)是否可以class支持任何形式的对对象状态进行操作的异步性?或者,它们是否仅适用于纯同步代码库?上面的例子在构造函数中,但它们不需要是..将问题推低一层..

class Foo {
    myMethod () {
      /* Can I do anything async here */
    }
}

或者,使用吸气剂...

class Foo {
    get myProp() {
        /* Is there any case that this is usefully asynchronous */
    }
}

我能想到的唯一例子是在同一个方法/构造函数/getter 中并行运行一些东西,但要在结论之前解决整个问题。我只是感到困惑,因为它似乎与完全异步库的所有推动,这只是为了混淆事情。除了教科书示例,我找不到它们有用的应用程序。

4

5 回答 5

68

我可不可以做async constructor()

不,这是一个语法错误 - 就像constructor* (). 构造函数是一种不返回任何东西(没有承诺,没有生成器)的方法,它只初始化实例。

而且,如果不是这样,构造函数应该如何工作

这样的构造函数根本不应该存在,请参阅让构造函数返回 Promise 是不好的做法吗?

ES6 类可以支持对对象状态进行操作的任何形式的异步吗?或者,它们是否仅适用于纯同步代码库?

是的,您可以在类上使用异步方法(即使使用建议的async语法),getter 也可以返回 Promise。

但是,您需要决定在某个异步进程仍处于活动状态时调用方法时应该发生什么。如果您希望它对您的所有操作进行排序,您应该将实例的状态存储在一个可以链接到该序列结束的 Promise 中。或者,如果您想允许并行操作,最好的方法是使您的实例不可变并为另一个实例返回一个承诺。

于 2016-05-31T22:11:43.350 回答
29

类可用于安排异步任务的另一种方法是独占使用静态方法

class Organizer {
    static async foo() {
        const data = await this.bar();
        data.key = value;
        return data;
    }
    static async bar() {
        return {foo:1, bar:2}
    }
};

Organizer.foo();

当然,这与创建一个简单的对象字面量或一个新文件并包含它没有什么不同,只是您可以更干净地extend处理它。

于 2016-05-31T23:59:54.787 回答
18

ECMAScript 2017 旨在成为异步方法的类。

调用另一个异步或返回承诺的函数是单行的!

无论延迟执行如何,高度表达的代码都可以从上到下不间断地读取

如果您有回调、替代错误处理程序、并行执行或其他未满足的需求,请在函数体中实例化 Promise。最好将代码放在函数体中而不是在 Promise 执行器中,并注意没有 try-catch 包装回调代码:在那里几乎什么都不做。

async 方法可以返回一个 Promise、一个常规值或 throw

Node.js 人曾经喜欢的回调 api,现在我们会恨之入骨:它们都必须被封装在 Promise 中

async/await 的美妙之处在于错误会隐式冒泡

class MyClass {
  async doEverything() {
    const sumOfItAll = await http.scrapeTheInternet() +
      await new Promise((resolve, reject) =>
        http.asyncCallback((e, result) => !e ? resolve(result) : reject(e)))
    return this.resp = sumOfItAll
  }
}

如果仅限于 ECMAScript 2015 并且没有异步,则返回承诺值:

class ES2015 {
  fetch(url) {
    return new Promise((resolve, reject) =>
      http.get(url, resolve).on('error', reject))
      .then(resp => this.resp = resp) // plain ECMAScript stores result
      .catch(e => { // optional internal error handler
        console.error(e.message)
        throw e // if errors should propagate
      })
  }
}

这个 ECMAScript 2015 版本是您真正要问的,任何所需的行为都可以使用返回的 promise 构造进行编码。

如果您真的非常想在构造函数中执行承诺,最好传入 then-catch 函数或提供一些回调构造,以便消费者可以对承诺履行或拒绝采取行动。在构造函数中,在执行实际工作之前等待 nextTick/.then 也是一个好习惯。

每个承诺都需要最后的收获,否则会有麻烦

于 2017-01-29T06:39:11.020 回答
2

无法将“异步”添加到构造函数的解决方法。当返回一个异步函数时,它就像返回一个承诺,所以构造函数本身不必是异步的。

class Foo {
  constructor() {
    return this.init()
  }
  async init() {
    this.res = await getHTML()
    return this
  }
}
const foo = await new Foo()

更短,但需要使用承诺

class Foo {
  constructor() {
    return new Promise(async resolve => {
      this.res = await getHTML()
      resolve(this)
    })
  }
}
const foo = await new Foo()
于 2021-09-19T17:01:00.820 回答
1

这是一个较晚的响应,但是您的第二个示例不起作用的原因是由于上下文错误。当您将 afunction () {}作为参数传递给 时Promise.prototype.then(),函数内部的词法this将是函数本身,而不是类。这就是为什么设置this.res似乎什么都不做:this在这种情况下,指的是函数自己的范围。

在 Javascript 中有几种访问外部作用域的方法,经典的一种(在 ES5 代码中经常看到)是:

class Foo {
  constructor() {
    var _this = this

    getHTML().then(function (res) {
      _this.res = res
    })
  }
}

通过对 class 的引用this,您可以在内部范围内访问它。

ES6 的做法是使用箭头函数,它不会创建新的作用域,而是“保留”当前的作用域。

class Foo {
  constructor() {
    getHTML().then(res => this.res = res)
  }
}

除了上下文问题,在我看来这仍然不是一个最佳的异步模式,因为你无法知道什么时候getHTML()完成,或者更糟的是,失败了。使用异步函数可以优雅地解决这个问题。虽然你不能创建一个async constructor () { ... },但你可以在构造函数中发起一个承诺,并await在依赖它的函数中发起一个承诺。

类构造函数中异步属性的示例要点

于 2018-01-13T15:50:36.243 回答