如何做异步 JavaScript 的 getter 和 setter? [英] How would one do async JavaScript getters and setters?
问题描述
想想 Rails,例如允许您定义一个与另一个相关联的属性:
class Customer
这不会为orders
设置数据库列.相反,它为 orders
创建了一个 getter,它允许我们做
@orders = @customer.orders
去获取相关的orders
对象.
在 JS 中,我们可以使用 getter 轻松做到这一点:
<代码>{姓名:约翰",获取订单(){//在这里获取订单内容}}
但是 Rails 是同步,而在 JS 中,如果在我们的示例中,按照合理的方式,我们要访问数据库,我们会异步.
我们将如何创建异步 getter(和 setter,就此而言)?
我们会返回一个最终得到解决的承诺吗?
<代码>{姓名:约翰",获取订单(){//创建一个承诺//db 和 promise 的伪代码...db.find("orders",{customer:"John"},function(err,data) {承诺.解决(数据);});回报承诺;}}
我们可以这样做
customer.orders.then(....);
或者我们会做更多的角度风格,我们会自动将它解析为一个值?
总而言之,我们如何实现异步 getter?
get
和 set
函数关键字似乎与 async
关键字.但是,由于 async
/await
只是对 Promise
s 的包装,您可以只使用 Promise
来制作你的函数await
-able".
注意:应该可以使用 Object.defineProperty
方法将 async
函数分配给 setter 或 getter.><小时>
吸气剂
Promise 与 getter 配合得很好.
在这里,我使用了 Node.js 8 内置的 util.promisify()
函数,该函数将节点样式回调(nodeback")转换为 Promise
单行.这使得编写一个 await
-able getter 变得非常容易.
var util = require('util');类 Foo {获取订单(){return util.promisify(db.find)("orders", {customer: this.name});}};//我们不能在异步函数之外使用 await(异步函数(){var bar = new Foo();bar.name = '约翰';//因为 getter 不能接受参数console.log(await bar.orders);})();
<小时>
二传手
对于二传手来说,这有点奇怪.
您当然可以将 Promise 作为参数传递给 setter 并在内部执行任何操作,无论您是否等待 Promise 完成.
然而,我想一个更有用的用例(把我带到这里的那个!)是使用 setter 然后 await
在 setter 的任何上下文中完成该操作从使用.不幸的是,这是不可能的,因为setter 函数的返回值被丢弃.
function makePromise(delay, val) {返回新的承诺(解决 => {setTimeout(() => resolve(val), delay);});}类设置测试{设置 foo(p) {返回 p.then(function(val) {//用 val 做一些需要时间的事情返回 makePromise(2000, val);}).then(console.log);}};var bar = new SetTest();var promiseValue = makePromise(1000, 'Foo');(异步函数(){等待(bar.foo = promiseValue);console.log('完成!');})();
在这个例子中,Done!
在 1
秒后打印到控制台,Foo
被打印 2代码> 秒后.这是因为
await
正在等待 promisedValue
被实现,它永远不会看到在 setter 中使用/生成的 Promise
.
Think of how Rails, e.g. allows you to define a property as associated with another:
class Customer < ActiveRecord::Base
has_many :orders
end
This does not set up a database column for orders
. Instead, it creates a getter for orders
, which allows us to do
@orders = @customer.orders
Which goes and gets the related orders
objects.
In JS, we can easily do that with getters:
{
name: "John",
get orders() {
// get the order stuff here
}
}
But Rails is sync, and in JS, if in our example, as is reasonable, we are going to the database, we would be doing it async.
How would we create async getters (and setters, for that matter)?
Would we return a promise that eventually gets resolved?
{
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;
}
}
which would allow us to do
customer.orders.then(....);
Or would we do it more angular-style, where we would automatically resolve it into a value?
To sum, how do we implement async getters?
The get
and set
function keywords seem to be incompatible with the async
keyword. However, since async
/await
is just a wrapper around Promise
s, you can just use a Promise
to make your functions "await
-able".
Note: It should be possible to use the Object.defineProperty
method to assign an async
function to a setter or getter.
getter
Promises work well with getters.
Here, I'm using the Node.js 8 builtin util.promisify()
function that converts a node style callback ("nodeback") to a Promise
in a single line. This makes it very easy to write an await
-able 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);
})();
setter
For setters, it gets a little weird.
You can of course pass a Promise to a setter as an argument and do whatever inside, whether you wait for the Promise to be fulfilled or not.
However, I imagine a more useful use-case (the one that brought me here!) would be to use to the setter and then await
ing that operation to be completed in whatever context the setter was used from. This unfortunately is not possible as the return value from the setter function is discarded.
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!');
})();
In this example, the Done!
is printed to the console after 1
second and the Foo
is printed 2
seconds after that. This is because the await
is waiting for promisedValue
to be fulfilled and it never sees the Promise
used/generated inside the setter.
这篇关于如何做异步 JavaScript 的 getter 和 setter?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!