Promise 作为 class 方法调用在解析时触发 object.then
Promise as a class method call triggers object.then upon resolving
我有一个包含数据库方法的 class,它被包装在处理属性访问的代理中。由于这个问题与承诺有关,这里是一个简化的示例代码,它重现了同样的问题:
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)
会导致 promise 认为它可能是返回的 promise,因此 .then()
被执行。
我发现的唯一解决方法是将解析响应包装在新数组或另一个对象中,如下所示:
static fetch(query = {}, opts = {}) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([new this(query)])
}, 500)
})
}
// Output
// 2
// <-_attributes
// {"test":"test"}
我想知道的是,这是正确的方法吗?对于这种副作用还有其他解决方案吗?
始终检查作为承诺结果传递给的所有对象,以查看它是否具有 then
属性。如果是,则该函数用于对条目进行排队以获得最终值。这就是为什么逻辑像
Promise.resolve()
.then(() => {
return Promise.resolve(45);
})
.then(result => {
console.log(result);
});
logs 45
而不是 promise 对象。由于 promise 对象有一个 .then
属性,它用于解包 promises 值。同样的行为发生在你的 resolve(new this(query))
案例中,因为它需要知道是否有要解包的值。
正如您所说,在您的 post 中评论,您当然可以使用非代理包装实例,例如
resolve({ value: new this(query) })
这将在 that 对象上检查 .then
而不是在你的代理上,但是你必须做 .value
才能真正获得代理,这可能会很痛苦。
归根结底,这是您必须做出的选择。
我有一个包含数据库方法的 class,它被包装在处理属性访问的代理中。由于这个问题与承诺有关,这里是一个简化的示例代码,它重现了同样的问题:
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)
会导致 promise 认为它可能是返回的 promise,因此 .then()
被执行。
我发现的唯一解决方法是将解析响应包装在新数组或另一个对象中,如下所示:
static fetch(query = {}, opts = {}) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([new this(query)])
}, 500)
})
}
// Output
// 2
// <-_attributes
// {"test":"test"}
我想知道的是,这是正确的方法吗?对于这种副作用还有其他解决方案吗?
始终检查作为承诺结果传递给的所有对象,以查看它是否具有 then
属性。如果是,则该函数用于对条目进行排队以获得最终值。这就是为什么逻辑像
Promise.resolve()
.then(() => {
return Promise.resolve(45);
})
.then(result => {
console.log(result);
});
logs 45
而不是 promise 对象。由于 promise 对象有一个 .then
属性,它用于解包 promises 值。同样的行为发生在你的 resolve(new this(query))
案例中,因为它需要知道是否有要解包的值。
正如您所说,在您的 post 中评论,您当然可以使用非代理包装实例,例如
resolve({ value: new this(query) })
这将在 that 对象上检查 .then
而不是在你的代理上,但是你必须做 .value
才能真正获得代理,这可能会很痛苦。
归根结底,这是您必须做出的选择。