Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
576 views
in Technique[技术] by (71.8m points)

promise - How would one do async JavaScript getters and setters?

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The get and set function keywords seem to be incompatible with the async keyword. However, since async/await is just a wrapper around Promises, 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 awaiting 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...