How would one do async JavaScript getters and setters?

前端 未结 3 652
一生所求
一生所求 2021-01-03 18:37

Think of how Rails, e.g. allows you to define a property as associated with another:

class Customer < ActiveRecord::Base
  has_many :orders
end

3条回答
  •  没有蜡笔的小新
    2021-01-03 19:30

    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');
    })();
    

提交回复
热议问题