0

我有一个包含 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()

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

示例输出如下:

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

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

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

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

4

1 回答 1

3

始终检查作为 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) })

这将检查.then对象而不是您的代理,但是您必须.value实际获取代理,这可能会很痛苦。

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

于 2017-08-28T20:08:27.673 回答