【问题标题】:Promise as a class method call triggers object.then upon resolvingPromise 作为类方法调用触发 object.then 解析
【发布时间】:2017-08-28 10:37:44
【问题描述】:

我有一个包含 db 方法的类,它封装在一个代理中,该代理处理对属性的访问。由于问题与承诺有关,因此这里有一个简化的示例代码,它重现了相同的问题:

const handler = {
  ownKeys(target) {
    return Object.keys(target._attributes)
  },
  get(target, property) {
    console.log(`<-${property}`) // <-- this logs what properties are being accessed
    if (typeof target[property] !== 'undefined') {
        return Reflect.get(target, property)
    }
    return Reflect.get(target._attributes, property)
  },
  set(target, property, value) {
    target._attributes[property] = value
    return true
  }
}
class User {
    static table = 'users'
    static fetch(query = {}, opts = {}) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve(new this(query))
          }, 500)
        })
    }
    constructor(attributes = {}) {
        this._attributes = attributes
        return new Proxy(this, handler)
    }
}
async function trigger() {
  const user = await User.fetch({test:'test'})
  console.log(JSON.stringify(user._attributes))
}
trigger()

一切正常,在测试期间,我向代理添加了打印输出以确定使用此类模型设计的性能影响,并且我注意到我的模型 get 是从承诺链中调用的。

示例输出如下:

<-then
<-_attributes
{"test":"test"}

我猜返回new this(query) 会导致promise 认为它可能是一个返回的promise,因此.then() 被执行。 我发现的唯一解决方法是将解析响应包装在新数组或另一个像这样的对象中:

static fetch(query = {}, opts = {}) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve([new this(query)])
    }, 500)
  })
}
// Output
// 2
// <-_attributes
// {"test":"test"}

我想知道的是,这是正确的方法吗?还有其他解决方案可以解决这种副作用吗?

【问题讨论】:

  • 您现在看到的行为到底有什么问题?传递给resolve 的值可能是“thenable”,因此它会检查.then 是否存在。这是意料之中的。
  • 没有错,我只是想知道这是否是预期的行为,有没有办法解决它。问题是我想防止对代理的不必要调用,因为它的性能在某些情况下是有问题的。
  • 使用 "new this(query) 解析会导致承诺认为可能是返回的承诺" - 是的,完全正确。 “...因此.then() 被执行” - 不,它没有被执行,因为它不存在。但它被访问,以 try 执行它,以防它是一个方法。
  • 为什么会出现这个问题,您需要解决什么问题?
  • 嗯..访问你是对的,我正在记录对属性的访问,我的错。这本身不是问题,但正如我在其他评论中所写的那样,当由于代理性能缓慢而使用大量承诺处理大型数据集时,它可能会成为问题。通过封装解析值来转义代理是一种可用的技巧,我只是想确保这是正确的方法,感谢 cmets..

标签: javascript ecmascript-6 es6-promise es6-proxy


【解决方案1】:

始终检查作为 promise 的结果传递给的所有对象,以查看它是否具有 then 属性。如果是,则该函数用于对条目进行排队以获得最终值。这就是为什么逻辑喜欢

Promise.resolve()
   .then(() => {
       return Promise.resolve(45);
   })
   .then(result => {
       console.log(result);
   });

记录45 而不是一个承诺对象。由于 Promise 对象具有 .then 属性,因此它用于解开 Promise 值。同样的行为发生在您的 resolve(new this(query)) 案例中,因为它需要知道是否有要解包的值。

正如你所说,在你的帖子中评论,你当然可以用非代理包装实例,例如

resolve({ value: new this(query) })

这将在 that 对象上而不是在您的代理上检查 .then,但是您必须执行 .value 才能真正获得代理,这可能会很痛苦。

归根结底,这是你必须做出的选择。

【讨论】:

  • 我会接受您的回答,因为它为手头的问题提供了一些启示。我怀疑这样的事情,但得到确认总是好的。无论如何,我只会在等待的承诺上使用析构函数,这样就可以解决问题,而不会产生丑陋的 .value。像这样: const [user] = await User.fetch({ username })
猜你喜欢
  • 1970-01-01
  • 2019-12-03
  • 1970-01-01
  • 2014-09-04
  • 1970-01-01
  • 2018-04-09
  • 2023-01-30
  • 1970-01-01
  • 2017-05-22
相关资源
最近更新 更多