想想Rails如何,例如允许您将属性定义为与另一个属性相关联:
class Customer < ActiveRecord::Base
has_many :orders
end
这不会为orders
设置数据库列。相反,它会为orders
创建一个getter,它允许我们执行
@orders = @customer.orders
哪个会获得相关的orders
个对象。
在JS中,我们可以轻松地使用getter:
{
name: "John",
get orders() {
// get the order stuff here
}
}
但是Rails是同步,而在JS中,如果在我们的例子中,合理的话,我们将进入数据库,我们将这样做 async 。 / p>
我们如何创建异步getter(以及setter)?
我们会回复最终解决的承诺吗?
{
name: "John",
get orders() {
// create a promise
// pseudo-code for db and promise...
db.find("orders",{customer:"John"},function(err,data) {
promise.resolve(data);
});
return promise;
}
}
这将允许我们做
customer.orders.then(....);
或者我们会采用更角度的方式,我们会自动将其解析为值吗?
总之,我们如何实现异步getter?
答案 0 :(得分:7)
get
和set
功能关键字似乎与async
关键字不兼容。但是,由于async
/ await
只是Promise
的包装,因此您可以使用Promise
使您的函数“await
- 能够”。
注意:应该可以使用Object.defineProperty
方法将async
函数分配给setter或getter。
承诺与吸气剂配合良好。
在这里,我使用Node.js 8内置util.promisify()
函数,该函数将节点样式回调(“nodeback”)转换为单行中的Promise
。这样就可以很容易地编写一个await
- getter。
var util = require('util');
class Foo {
get orders() {
return util.promisify(db.find)("orders", {customer: this.name});
}
};
// We can't use await outside of an async function
(async function() {
var bar = new Foo();
bar.name = 'John'; // Since getters cannot take arguments
console.log(await bar.orders);
})();
对于制定者来说,它有点奇怪。
你当然可以将一个Promise传递给一个setter作为一个参数并在里面做任何事情,无论你是否等待Promise得到满足。
但是,我想一个更有用的用例(将我带到这里的那个!)将用于setter,然后await
该操作将在任何上下文中使用setter从。遗憾的是,这是不可能的,因为setter函数的返回值被丢弃了。
function makePromise(delay, val) {
return new Promise(resolve => {
setTimeout(() => resolve(val), delay);
});
}
class SetTest {
set foo(p) {
return p.then(function(val) {
// Do something with val that takes time
return makePromise(2000, val);
}).then(console.log);
}
};
var bar = new SetTest();
var promisedValue = makePromise(1000, 'Foo');
(async function() {
await (bar.foo = promisedValue);
console.log('Done!');
})();
在此示例中,Done!
在1
秒后打印到控制台,Foo
在此之后打印2
秒。这是因为await
正在等待promisedValue
完成,并且它永远不会在设置器中看到Promise
使用/生成。
答案 1 :(得分:0)
对于异步获取器,您可以执行以下操作:
const object = {};
Object.defineProperty(object, 'myProperty', {
async get() {
// Your awaited calls
return /* Your value */;
}
});
当涉及到异步设置器时,就会出现问题。
由于表达式a = b
总是产生b
,因此无法避免这种情况,即,持有属性a
的对象中没有设置器可以覆盖此行为。
由于我也偶然发现了这个问题,因此我发现异步设置器实际上是不可能的。因此,我意识到我必须选择一种替代设计来代替异步设置器。然后我想到了以下替代语法:
console.log(await myObject.myProperty); // Get the value of the property asynchronously
await myObject.myProperty(newValue); // Set the value of the property asynchronously
我使用以下代码,
function asyncProperty(descriptor) {
const newDescriptor = Object.assign({}, descriptor);
delete newDescriptor.set;
let promise;
function addListener(key) {
return callback => (promise || (promise = descriptor.get()))[key](callback);
}
newDescriptor.get = () => new Proxy(descriptor.set, {
has(target, key) {
return Reflect.has(target, key) || key === 'then' || key === 'catch';
},
get(target, key) {
if (key === 'then' || key === 'catch')
return addListener(key);
return Reflect.get(target, key);
}
});
return newDescriptor;
}
返回一个异步属性的描述符,给定另一个描述符,它可以定义一个看起来像异步设置器的东西。
您可以按以下方式使用上面的代码:
function time(millis) {
return new Promise(resolve => setTimeout(resolve, millis));
}
const object = Object.create({}, {
myProperty: asyncProperty({
async get() {
await time(1000);
return 'My value';
},
async set(value) {
await time(5000);
console.log('new value is', value);
}
})
});
一旦您设置了如上所述的异步属性,就可以按照图示进行设置:
(async function() {
console.log('getting...');
console.log('value from getter is', await object.myProperty);
console.log('setting...');
await object.myProperty('My new value');
console.log('done');
})();
答案 2 :(得分:0)
以下允许代理处理程序中的异步设置器遵循 Davide Cannizzo 的回答中的约定。
var obj = new Proxy({}, asyncHandler({
async get (target, key, receiver) {
await new Promise(a => setTimeout(a, 1000))
return target[key]
},
async set (target, key, val, receiver) {
await new Promise(a => setTimeout(a, 1000))
return target[key] = val
}
}))
await obj.foo('bar') // set obj.foo = 'bar' asynchronously
console.log(await obj.foo) // 'bar'
function asyncHandler (h={}) {
const getter = h.get
const setter = h.set
let handler = Object.assign({}, h)
handler.set = () => false
handler.get = (...args) => {
let promise
return new Proxy(()=>{}, {
apply: (target, self, argv) => {
return setter(args[0], args[1], argv[0], args[2])
},
get: (target, key, receiver) => {
if (key == 'then' || key == 'catch') {
return callback => {
if (!promise) promise = getter(...args)
return promise[key](callback)
}
}
}
})
}
return handler
}
答案 3 :(得分:-2)
以下是如何实施获取订单功能
function get(name) {
return new Promise(function(resolve, reject) {
db.find("orders", {customer: name}, function(err, data) {
if (err) reject(err);
else resolve(data);
});
});
}
您可以将此功能称为
customer.get("John").then(data => {
// Process data here...
}).catch(err => {
// Process error here...
});