【问题标题】:Make await return thenable, or infinite recursion in awaiting awaitable使 await 返回 thenable,或者在 awaitable 中无限递归
【发布时间】:2020-01-12 15:53:46
【问题描述】:

尝试提出 API,混合承诺功能,例如:

class Awaitable {
  constructor () {
    this.promise = Promise.resolve()
  }
  then (fn) {
    // awaited result must be _this_ instance
    return this.promise.then(() => fn(this))
  }
}

let smth = await (new Awaitable())
console.log(smth)

此代码创建递归。 重点是让smth 成为新创建的thenable 实例。

thennull 存根会使等待的结果不完整。

我想知道这是否可能,似乎存在一些概念上的障碍,我无法绕过。

【问题讨论】:

  • 不,你不能自己解决一个承诺。
  • 它似乎不仅与它本身一样,而且与任何 thenable 一样。有什么解决办法吗?
  • 你真正的主要观点是什么?你想在这里做什么?为什么需要Awaitable 课程?你不想想要创建递归,对吧?
  • 想象 jQuery 在所有效果完成后可以等待并返回集合 let $target = await $(target).fadeIn(100)
  • jQuery 包装器实际上 可等待的,它只需要调用一个额外的方法:.promise()。但由此产生的承诺通过undefined afaik 实现,而不是 jQuery 包装器本身。

标签: javascript promise async-await es6-promise ecmascript-2017


【解决方案1】:

promise(或任何 thenable)自行实现是没有意义的。如果您已经拥有该对象,则无需等待。

Resolving a promise does automatically unwrap thenables,这将在您自己解决的情况下创建无限递归,而您can (unfortunately) not avoid that。所以不要试图这样做。兑现你的承诺,一无所有 (undefined)。

class Awaitable {
  constructor () {
    this.promise = Promise.resolve(undefined); // simplified
  }
  then(onfulfill, onreject) {
    return this.promise.then(onfulfill, onreject);
  }
  // other methods
}

const smth = new Awaitable();
await smth; // works just fine now
console.log(smth);

【讨论】:

  • 是的,但是以通常的方式,const smth = Promise.resolve(undefined); 还没有实现什么?也许如果 OP 可以解释“组织此类 API 的模式”......
  • @Roamer-1888 我希望这个东西能够包装一个更复杂的承诺,并拥有比then更多的方法。否则,是的,人们只会使用简单的承诺。
  • @Roamer-1888 模式我的意思是let s = await (new Something()).asyncA().asyncB() - 这对于组织每个实例的任务队列非常方便。这可以是任何东西——从数据库连接器到后端 API。特别是,我在github.com/spectjs/spect 中实现了这一点。我想我会尝试动态的then 方法——如果有一些计划,那么实例是可以的,否则不是——await 不会导致递归。我会尽力想出一个答案。
  • @dy_,与您在此处提出的问题相切,您可能对Fantasy Land Specification 感兴趣。
【解决方案2】:

正确的解决方案

Symbol.thenable 提案。

import { parse as parseStack } from 'stacktrace-parser' 

class Awaitable {
  constructor () {
    this.promise = Promise.resolve()
  },

  [Symbol.thenable]: false,

  then(fn) {
    this.promise.then(() => fn(this))
    return this
  }
}

let smth = await (new Awaitable())
console.log(smth.then) // function

易碎non-standard解决方案

可以通过解析调用堆栈来检测thenable 实例是否被await 调用。这是基于stacktrace-parser包的解决方案:

import { parse as parseStack } from 'stacktrace-parser' 

class Awaitable {
  constructor () {
    this.promise = Promise.resolve()
  }
}

Object.defineProperty(Awaitable.prototype, 'then', {
  get() {
    let stack = parseStack((new Error).stack)
    
    // naive criteria: if stacktrace is leq 3, that's async recursion, bail out
    // works in webkit, FF/nodejs needs better heuristic
    if (stack.length <= 3) return null
    
    return (fn) => {
      this.promise.then(() => {
        fn(this)
      })
      return this
    }
  }
})

let smth = await (new Awaitable())
console.log(smth.then) // function

必须为 FF/nodejs 增强启发式,以巩固 - 这需要某种静态分析向导。

【讨论】:

    猜你喜欢
    • 2021-09-27
    • 1970-01-01
    • 2018-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-21
    • 1970-01-01
    • 2017-10-19
    相关资源
    最近更新 更多