如何进行异步 JavaScript getter 和 setter?

问题描述 投票:0回答:5

想想 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 是sync,而在 JS 中,如果在我们的示例中,合理地说,我们要访问数据库,那么我们将执行async

我们如何创建异步 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?

javascript promise
5个回答
30
投票

get
set
函数关键字似乎与
async
关键字不兼容。但是,由于
async
/
await
只是
Promise
的包装,因此您可以仅使用
Promise
来使您的函数“能够”。

注意:应该可以使用

await 方法将

Object.defineProperty
函数分配给 setter 或 getter。


吸气剂

Promise 与 getters 配合得很好。

在这里,我使用 Node.js 8 内置

async

函数,在一行中将节点样式回调(“nodeback”)转换为

util.promisify()
。这使得编写一个
Promise
可用的 getter 变得非常容易。

await


二传手

对于二传手来说,这有点奇怪。

你当然可以将 Promise 作为参数传递给 setter,并在里面做任何事情,无论你是否等待 Promise 被履行。

但是,我想象一个更有用的用例(将我带到这里的用例!)是使用 setter,然后

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 的任何上下文中完成该操作。不幸的是,这是不可能的,因为

setter 函数的返回值被丢弃
await

在此示例中,
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
被满足,并且它永远不会看到在 setter 中使用/生成的
promisedValue
    


4
投票

Promise

但是,表达式 
const object = {}; Object.defineProperty(object, 'myProperty', { async get() { // Your awaited calls return /* Your value */; } });

始终会生成

a = b
,无法使用熟悉的语法创建异步 setter。
您将不得不接受替代设计,例如:

b

可以按如下方式实现,

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); let promise; delete newDescriptor.set; 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) { return key === 'then' || key === 'catch' ? addListener(key) : Reflect.get(target, key); } }); return newDescriptor; }



0
投票

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 () => { console.log('getting...'); console.log('value from getter is', await object.myProperty); console.log('setting...'); await object.myProperty('My new value'); console.log('done'); })();



0
投票
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 }

的使用(这是 TypeScript,只需删除设置返回值类型以获取 JS 的

await
位即可):
: Promise<..>



-5
投票

// this doesn't work private get async authedClientPromise(): Promise<nanoClient.ServerScope> { await this.makeSureAuthorized() return this.client } // but this does private get authedClientPromise(): Promise<nanoClient.ServerScope> { return (async () => { await this.makeSureAuthorized() return this.client })() }

你可以这样调用这个函数

function get(name) { return new Promise(function(resolve, reject) { db.find("orders", {customer: name}, function(err, data) { if (err) reject(err); else resolve(data); }); }); }

© www.soinside.com 2019 - 2024. All rights reserved.