Is it possible to make a getter method async? [duplicate] - javascript

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 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.

As for asynchronous getters, you may just do something like this:
const object = {};
Object.defineProperty(object, 'myProperty', {
async get() {
// Your awaited calls
return /* Your value */;
}
});
Rather, the problem arises when it comes to asynchronous setters.
Since the expression a = b always produce b, there is nothing one can do to avoid this, i.e. no setter in the object holding the property a can override this behavior.
Since I stumbled upon this problem as well, I could figure out asynchronous setters were literally impossible. So, I realized I had to choose an alternative design for use in place of async setters. And then I came up with the following alternative syntax:
console.log(await myObject.myProperty); // Get the value of the property asynchronously
await myObject.myProperty(newValue); // Set the value of the property asynchronously
I got it working with the following code,
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;
}
which returns a descriptor for an asynchronous property, given another descriptor that is allowed to define something that looks like an asynchronous setter.
You can use the above code as follows:
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);
}
})
});
Once you've set up with an asynchronous property like the above, you can set it as already illustrated:
(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');
})();

The following allows for async setters in proxy handlers following the convention in Davide Cannizzo's answer.
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
}

Here's another approach to this. It creates an extra wrapper, but in other aspects it covers what one would expect, including usage of await (this is TypeScript, just strip the : Promise<..> bit that sets return value type to get JS):
// 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
})()
}

Here's how you could implement your get orders function
function get(name) {
return new Promise(function(resolve, reject) {
db.find("orders", {customer: name}, function(err, data) {
if (err) reject(err);
else resolve(data);
});
});
}
You could call this function like
customer.get("John").then(data => {
// Process data here...
}).catch(err => {
// Process error here...
});

Related

RxJs Observable created from Iterable of promises not updating values each iteration

I would like to create an RxJS Observable from an iterable like the following:
const networkIterableFactory = (resource: string) => {
let i = 0;
return {
[Symbol.iterator]() {
return {
next() {
return {
done: false,
value: fetch(resource, {
mode: 'cors',
}).then(async response => {
console.log('i = ', i);
await throttle(10000); // Do some stuff
i++;
return {i: 'i'};
}),
};
},
};
},
};
};
function throttle(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
let networkIterable = networkIterableFactory('google.com');
let network$ = rxjs.from(networkIterable).pipe(rxjs.operators.take(5));
network$.subscribe(() => console.log('yo!'));
Issue is that i prints 5 times as 0. It seems as though the way that the iterable's iterator saves its state is through updating the outer closure. rxjs.from just takes the whole iterable as one emmission so a bunch of unresolved promises are returned, but I need the iterator state to be altered by logic within the promise callback. Is there a way to make the observable wait until the promise resolves before emitting the next item from the iterator? I would rather avoid using asyncIterable because I don't want to bring in IxRx.
Since the values of your iterable are returned asynchronously, then you should implement Symbol.asyncIterator instead of Symbol.iterator Try this instead:
const networkIterableFactory = (resource: string) => {
let i = 0;
return {
[Symbol.asyncIterator]() {
return {
next() {
return fetch(resource, { mode: 'cors' }).then(x => ({ done: false, value: x }));
},
};
},
};
};
function throttle(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
let networkIterable = networkIterableFactory('google.com');
let network$ = rxjs.from(networkIterable).pipe(rxjs.operators.take(5));
network$.subscribe(() => console.log('yo!'));
Edit:
RxJS actually doesn't support Async iterators yet: https://github.com/ReactiveX/rxjs/issues/1624.
I also tried with an AsyncGenerator:
const { from } = require('rxjs');
async function* test() {};
const asyncGenerator = test();
from(asyncGenerator);
But it throws:
TypeError: You provided an invalid object where a stream was expected.
You can provide an Observable, Promise, Array, or Iterable.
So you won't be able to make it with this pattern, I actually believe that RxJS is not suited for "pulling" data like you do with take (If this pattern worked, it would end up in an infinite requests loop, even if you only take 5 results). It is rather designed to "push" things to whoever listens.

How would one do async JavaScript setters?

I'm trying to do asynchronous setters using async/await keywords.
Here some fakes database functions that takes time
function getProjectFromDatabase() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('My cool project!'), 500) // 500ms latency
});
}
function setProjectToDatabase(projectName) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('set!'), 500) // 500ms latency
});
}
Here is an example of implementation for the user
let user = {
// Getter
get project() {
return (async () => {
return await getProjectFromDatabase();
})();
},
// Setter
set project(projectName) {
return (async () => {
return await setProjectToDatabase(projectName);
})();
},
// Method
setProject(projectName) {
return (async () => {
return await setProjectToDatabase(projectName);
})();
}
};
And here is an example of use
(async function() {
console.log(await user.project); // Getter works!
await user.setProject('My new cool project!'); // Method works!
await (user.project = 'Another project'); // Setter doesn't work...
})();
But the return value from the setter function seems ignored.
How could I do that?
The assignment expression always evaluates to its right-hand side.
a.b.c = "This is what it gets evaluated to"
there is no way to change that.
Normally a setter sets some value and you don't care about the return value. In a traditional object this would be some other property (probably not intended to be used directly). Translated to something like a DB, the setter would post an INSERT or UPDATE and the new value would be set.
Your code in the setter isn't actually setting anything. If this were an actual DB call you would save some value in the database and then the next call to the getter would produce the new value. If you add something like this to the fan DB call, you will get something closer to traditional getter/setter logic:
const a_user = {project: 'My cool project!'} // some fake db object
function getProjectFromDatabase() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(a_user.project), 500) // 500ms latency
});
}
function setProjectToDatabase(projectName) {
return new Promise((resolve, reject) => {
setTimeout(() => {
a_user.project = projectName // actually set something
resolve(a_user.project) // caller of setter doesn't care about return value, but we still need to resolve so async works.
}, 500) // 500ms latency
});
}
let user = {
// Getter
get project() {
return (async () => {
return await getProjectFromDatabase();
})();
},
// Setter
set project(projectName) {
return (async () => {
return await setProjectToDatabase(projectName);
})();
},
};
(async function() {
console.log(await user.project); // Getter works!
await (user.project = 'Another project'); //
console.log(await user.project); // new value has been set
})();

Simple Promise and Then implementation

Recently I was shown a piece of code that were asked during a full-stack developer interview.
It involved creating a Promise, in which the candidate should implement,
passing it a resolve function, and chaining 2 then's.
I tried implementing the Promise very naively only to make the code work.
Created a ctor that accepts a resolver func,
Created a Then function that accepts a callback and returns a Promise,
and simply calls the callback on the resolver function.
class MyPromise {
constructor(resolver) {
this.resolver = resolver;
}
then(callback) {
const result = new MyPromise(callback);
this.resolver(callback);
return result;
}
}
promise = new MyPromise(
(result) => {
setTimeout(result(2), 500);
});
promise.then(result => {
console.log(result);
return 2 * result;
}).then(result => console.log(result));
The expected result is 2,4 - just like operating with real Promise.
But i'm getting 2,2.
I'm having trouble figuring out how to get the return value for the first "then" and passing it along.
Here's the shortened code for creating a promise class,
class MyPromise {
constructor(executor) {
this.callbacks = [];
const resolve = res => {
for (const { callback } of this.callbacks) {
callback(res);
}
};
executor(resolve);
}
then(callback) {
return new MyPromise((resolve) => {
const done = res => {
resolve(callback(res));
};
this.callbacks.push({ callback: done });
});
}
}
promise = new MyPromise((resolve) => {
setTimeout(() => resolve(2), 1000);
});
promise.then(result => {
console.log(result);
return 2 * result;
}).then(result => console.log(result));
Your question has some issues:
The r2 variable is nowhere defined. I will assume result was intended.
The setTimeout is doing nothing useful, since you execute result(2) immediately. I will assume setTimeout(() => result(2), 500) was intended.
If the code was really given like that in the interview, then it would be your job to point out these two issues before doing anything else.
One issue with your attempt is that the promise returned by the then method (i.e. result) is never resolved. You need to resolve it as soon as the this promise is resolved, with the value returned by the then callback.
Also, the promise constructor argument is a function that should be executed immediately.
In the following solution, several simplifications are made compared to a correct Promise behaviour.
It does not call the then callbacks asynchronously;
It does not support multiple then calls on the same promise;
It does not provide the rejection path;
It does not prevent a promise from resolving twice with a different value;
It does not deal with the special case where a then callback returns a promise
console.log("Wait for it...");
class MyPromise {
constructor(executor) {
executor(result => this.resolve(result));
}
resolve(value) {
this.value = value;
this.broadcast();
}
then(onFulfilled) {
const promise = new MyPromise(() => null);
this.onFulfilled = onFulfilled;
this.resolver = (result) => promise.resolve(result);
this.broadcast();
return promise;
}
broadcast() {
if (this.onFulfilled && "value" in this) this.resolver(this.onFulfilled(this.value));
}
};
// Code provided by interviewer, including two corrections
promise = new MyPromise(
(result) => {
setTimeout(()=>result(2), 500); // don't execute result(2) immediately
});
promise.then(result => {
console.log(result); // Changed r2 to result.
return 2 * result;
}).then(result => console.log(result));
Note the 500ms delay in the output, which is what should be expected from the (corrected) setTimeout code.
I posted a full Promises/A+ compliant promise implementation with comments in this answer
How about very simple:
const SimplePromise = function(cb) {
cb(
data =>
(this.data = data) &&
(this.thenCb || []).forEach(chain => (this.data = chain(this.data))),
error =>
(this.error = error) &&
(this.catchCb || []).forEach(chain => (this.error = chain(this.error)))
);
this.then = thenCb =>
(this.thenCb = [...(this.thenCb || []), thenCb]) && this;
this.catch = catchCb =>
(this.catchCb = [...(this.catchCb || []), catchCb]) && this;
};
Example Here: https://codesandbox.io/s/0q1qr8mpxn
Implying that this r2 is actually the result parameter.
The problem with your code is that you do not retrieve the result from the result(2). The first "then" gets executed, prints 2, return 4, but this 4 is just wasted.
I wrote some code with synchronous function only to demonstrate what to do if you want to get this 2,4 output:
class MyPromise {
constructor(resolver) {
this.resolver = resolver;
}
then(callback) {
var res = callback(this.resolver());
var result = new MyPromise(() => { return res; });
return result;
}
}
let promise = new MyPromise(
() => {
return 2;
});
promise
.then(result => {
console.log(result);
return 2 * result;
})
.then(result => {
console.log(result);
});
If you want the resolver to be somewhat async you should use Promises (because return value from function executed inside setTimeout can be retrieved, see
here.
If you are not allowed to use these built-in Promises, you can write them yourself with some dummy deferred object and await them (setInterval) to resolve (should be basically the same logic).
There are a few problems with your original code. Notably you are only executing the constructor argument upon call of the then method and you aren't actually chaining the outputs of the 'then' callbacks.
Here is a very (very!) basic promise implementation based upon adapting your example. It will also work for cases where 'then' is called after the promise is resolved (but not if 'then' is already called - multiple then blocks is not supported).
class MyPromise {
constructor(resolver) {
let thisPromise = this;
let resolveFn = function(value){
thisPromise.value = value;
thisPromise.resolved = true;
if(typeof thisPromise.thenResolve === "function"){
thisPromise.thenResolve();
}
}
if (typeof resolver === "function") {
resolver(resolveFn);
}
}
then(callback) {
let thisPromise = this;
thisPromise.thenFn = callback;
return new MyPromise((resolve) =>{
thisPromise.thenResolve = () => {
thisPromise.value = thisPromise.thenFn(thisPromise.value);
resolve(thisPromise.value);
}
//automatically resolve our intermediary promise if
//the parent promise is already resolved
if(thisPromise.resolved){
thisPromise.thenResolve();
}
});
}
};
//test code
console.log("Waiting for Godot...");
promise = new MyPromise((resolve) =>{
setTimeout(()=>{
resolve(2)
},500);
});
promise.then((result) => {
console.log(result);
return 2 * result;
}).then((result) => {
console.log(result);
return 2 * result;
}).then((result) => {
console.log(result)
});

How to Inspect fetch call and Return same Call

I need a way of taking a promise, calling .then on it to inspect the returned value, and then to return this promise exactly as it was to other parts of the system. The context is I'm trying to modify the fetch API from a GraseMonkey script so that I can modify the data returned. I inspect this by calling .json() on the response. However if I don't modify the data, I need to return an object exactly representing the original call to the fetch API so that the page's code sees no difference. But when I have tried returning the object, I get an error that the response has already been consumed and now I'm lost in a stack of Promises that I can't seem to get to work out (I'm not a JS native)
The code below is what I have already and it works, but it's not really acceptable as it duplicates every other request that's not being mangled.
function newFetch(){
if (needsToBeModified(arguments[0])) {
response = null;
return oldFetch.apply(this, arguments)
.then(r => {
response = r;
return r.json();
})
.then(
j => {
j = processJSON(j);
return new Promise((resolve, rej) => {
response.json = () => new Promise((resolve, reject) => resolve(j));
resolve(response);
});
},
(fail) => {
return oldFetch.apply(this, arguments)
//How can I avoid making this call here again?
}
);
} else {
return oldFetch.apply(this, arguments);
}
}
Please could someone tell me a way of peeking at the json, and if this throws an error, returning the original promise from fetch without needing to make the call again?
Thanks.
fetch() returns a promise that resolves to a response object. One of the methods on that response object is .clone() which sounds like it does just what you want. From the doc for .clone():
The clone() method of the Response interface creates a clone of a response object, identical in every way, but stored in a different variable.
clone() throws a TypeError if the response Body has already been used. In fact, the main reason clone() exists is to allow multiple uses of Body objects (when they are one-use only.)
I think you can use that like this:
function newFetch(){
let p = oldFetch.apply(this, arguments);
if (needsToBeModified(arguments[0])) {
let origResponse, cloneResponse;
return p.then(r => {
origResponse = r;
cloneResponse = r.clone();
return r.json();
}).then(j => {
j = processJSON(j);
// monkey patch .json() so it is a function that resolves to our modified JSON
origResponse.json = () => Promise.resolve(j);
return origResponse;
}, fail => {
// return clone of original response
if (cloneResponse) {
return cloneResponse;
} else {
// promise was rejected earlier, don't have a clone
// need to just propagate that rejection
throw fail;
}
});
} else {
return p;
}
}
Like #jfriend suggested, you will need to clone the response so that your caller can consume it again after you inspected the JSON:
function newFetch(urlOrRequest) {
var promise = oldFetch.apply(this, arguments);
if (needsToBeModified(urlOrRequest)) {
return promise.then(response =>
response.clone().json().then(obj => {
const result = Promise.resolve(processJSON(obj));
response.json = () => result;
return response;
}, jsonErr => {
return response;
})
);
} else {
return promise;
}
}
Or better yet, instead of returning the old, unaltered response with a patched json method, just create a new one:
function newFetch(urlOrRequest) {
var promise = oldFetch.apply(this, arguments);
if (needsToBeModified(urlOrRequest)) {
return promise.then(response =>
response.clone().json().then(obj => {
const result = JSON.stringify(processJSON(obj));
return new Response(result, response);
}, jsonErr => response)
);
} else {
return promise;
}
}
Alternatively, the easiest way to go would be to defer your processJSON call into the overwritten json method:
…
if (needsToBeModified(urlOrRequest)) {
return promise.then(response =>
response.json = function() {
return Response.prototype.json.call(this).then(processJSON);
};
return response;
});
}
…
I think I understand that question to say that sometimes you need to alter the result of an async operation depending it's arguments
There's no need to perform the operation twice, and I think the OP code can be dramatically simplified as follows:
function newFetch() {
return oldFetch.apply(this, arguments).then(r => {
return (needsToBeModified(arguments[0]))? processJSON(r.json()) : r;
});
}

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?
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.
As for asynchronous getters, you may just do something like this:
const object = {};
Object.defineProperty(object, 'myProperty', {
async get() {
// Your awaited calls
return /* Your value */;
}
});
Rather, the problem arises when it comes to asynchronous setters.
Since the expression a = b always produce b, there is nothing one can do to avoid this, i.e. no setter in the object holding the property a can override this behavior.
Since I stumbled upon this problem as well, I could figure out asynchronous setters were literally impossible. So, I realized I had to choose an alternative design for use in place of async setters. And then I came up with the following alternative syntax:
console.log(await myObject.myProperty); // Get the value of the property asynchronously
await myObject.myProperty(newValue); // Set the value of the property asynchronously
I got it working with the following code,
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;
}
which returns a descriptor for an asynchronous property, given another descriptor that is allowed to define something that looks like an asynchronous setter.
You can use the above code as follows:
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);
}
})
});
Once you've set up with an asynchronous property like the above, you can set it as already illustrated:
(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');
})();
The following allows for async setters in proxy handlers following the convention in Davide Cannizzo's answer.
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
}
Here's another approach to this. It creates an extra wrapper, but in other aspects it covers what one would expect, including usage of await (this is TypeScript, just strip the : Promise<..> bit that sets return value type to get JS):
// 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
})()
}
Here's how you could implement your get orders function
function get(name) {
return new Promise(function(resolve, reject) {
db.find("orders", {customer: name}, function(err, data) {
if (err) reject(err);
else resolve(data);
});
});
}
You could call this function like
customer.get("John").then(data => {
// Process data here...
}).catch(err => {
// Process error here...
});

Categories

Resources