JavaScript ES6 类 对异步代码库有用吗?

Are JavaScript ES6 Classes of any use with asynchronous code bases?

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

我能做吗:

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

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

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

如果这些模式都不起作用,那么 ES6 class 中的构造函数(以及 classes)是否可以支持对对象状态进行操作的任何形式的异步性?或者,它们是否仅适用于纯同步代码库?上面的例子在constructor里面,其实没必要..把问题往下推一层..

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

或者,getter...

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

我能想到的唯一例子是 运行 在同一个 method/constructor/getter 中并行处理某事,但在得出结论之前解决整个问题。我只是感到困惑,因为似乎所有对完全异步库的推动,这只会让事情变得混乱。除了教科书的例子,我找不到一个有用的应用程序。

Can I do async constructor()

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

And, if not how should a constructor work that does this

这样的构造函数根本不应该存在,见Is it bad practice to have a constructor function return a Promise?

Can ES6 classes support any form of asynchrony that operates on the object's state? Or, are they only for purely synchronous code bases?

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

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

类 用于安排异步任务的另一种方法是专门使用 static methods

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 它。

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

调用另一个异步或 promise-returning 函数是 one-liner!

无论延迟执行如何,从上到下不间断地读取高表现力的代码

如果您有回调、替代错误处理程序、并行执行或其他未满足的需求,请在函数体中实例化承诺。最好在函数体中而不是在 promise 执行器中包含代码,并注意没有 try-catch 包装回调代码:do next-to-nothing there.

异步方法可以return一个承诺,一个常规值,或者抛出

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 并且没有异步,return 承诺值:

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 版本是您真正要问的,任何需要的行为都可以使用 returned promise 构造进行编码。

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

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

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

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

class Foo {
  constructor() {
    var _this = this

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

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

ES6 的做法是使用 arrow functions,它不会创建新范围,而是 "keep" 当前范围。

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

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

Example gist class 构造函数中的异步 属性。

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

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

更短但需要使用 promise

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