JavaScript proxy return async value on "get"

3.9k Views Asked by At

I have some objects that I am fetching from a web server. Some of the attributes of the objects require additional async parsing, but I want to do that lazily for performance reasons. I am trying to use a proxy to intercept the access and then parse the attribute if accessed. My handler would look something like this

class MyClass {
   type() {
      return "my new class";
   }
}

let myObject = new MyClass();

let proxy = new Proxy(myObject, {
  get: async(target, prop, receiver) => {
    if (prop === "attribute") {
      let newProp = prop + "_parsed";
      if (!(newProp in target)) {
        return await (new Promise((resolve, reject) => { resolve("parsed value") }));
      }
      else {
        return target[newProp];
      }
    }
    else {
      return Reflect.get(target, prop, receiver);
    }
  },
});

console.log(proxy.attribute);  // "parsed value"
console.log(proxy.type()); // "my new class"

If this worked, I would expect to see

parsed value
my new class

but I am getting the following:

{}
{
  "message": "Uncaught TypeError: proxy.type is not a function",
  "filename": "https://stacksnippets.net/js",
  "lineno": 41,
  "colno": 19
}

Is there any way I can return an asynchronous value through a proxy but have the calling code think it is just accessing an attribute? I am even open to forcing the "parse" logic (represented by the promise) to be synchronous, however that has portions that are async so I am not sure how to do that.

1

There are 1 best solutions below

3
On

You cannot (should not) use an async method for the get trap. These always return promises, and you want .type to return a function. You'd want to use

async function parseValue() {
  await new Promise(resolve => setImmediate(resolve));
  return "parsed value";
}
let proxy = new Proxy(myObject, {
  get(target, prop, receiver) {
    if (prop === "attribute") {
      let newProp = prop + "_parsed";
      if (!(newProp in target)) {
        target[newProp] = parseValue();
      }
      return target[newProp];
    } else {
      return Reflect.get(target, prop, receiver);
    }
  },
});
…
console.log(await proxy.attribute);
console.log(proxy.type());