Awaited Assign operation

Proposal: GitHub - wentout/awaited-assign: async set feature

Help me please to convert it to https://github.com/tc39/awaited-assignment

Let consider the following JavaScript code piece we may use on modern engines today:

'use strict';

const myObj = {};
let field = 123;

Object.defineProperty( myObj, 'field', {
	async get () {
		return new Promise( ( resolve, reject ) => {
			setTimeout( () => {
				resolve( field );
			}, 1000 );
		} );
	},
	async set ( value ) {
		new Promise( ( resolve, reject ) => {
			setTimeout( () => {
				field = value;
				resolve( field );
			}, 100 );
		} );
	}
} );

This code example above is fully functional and working at least via V8 runtime. Having Async Getters for today we allowed do consder that Setter is also asynchronous, but, unfortunately while being asynchronous indeed we are not allowed to track this operation. So having this code piece below is a scenario when we lack of instrumentation:

( async () => {
	console.log( 'initial : ', await myObj.field );
	console.log( 'the moment of assignment invocation : ', myObj.field = 321 );
	console.log( 'real value during changes happening : ', field );
	console.log( 'reading assigned value after change : ', await myObj.field );
	console.log( 'real value when changes indeed made : ', field );
} )();

Here we see the moment between assignment operator invocation and real value change today is unpredictable.

Thus let assume we may use the following construct to keep eye on this somehow:

await myObj.field = value;

This means Assignment Operation may have Asynchronous Behaviour for consistency with Async Getter

This is already valid JS, the assignment will return the RHS value. e.g:

let obj = {};
console.log(await obj.foo = Promise.resolve(42)); // logs 42

EDIT: I'm wrong. To be valid syntax today it needs to be await (obj.foo = v)

Thank you @aclaymore
Though anyway you aware of right-hand side of assignment operator, where I aware of left-hand side. As we already got async setters then how to track that setter promise. So I wish it returned on operation you are reviewing.

Assignment maps to the [[set]] method of meta-object-protocol, which internally does already have a return value: a boolean.

The true/false denotes if the assignment was successful or not. In strict-mode, the assignment returning false will lead to an exception. In sloppy mode it is a silent failure.

main();
function main() {
    "use strict";
    const obj = {};
    console.log(Reflect.set(obj, "foo", 1)); // true
    Object.freeze(obj);
    console.log(Reflect.set(obj, "bar", 1)); // false
    try {
        obj.bar = 1;
    } catch (err) {
        console.log(err.constructor.name) // TypeError
    }  
}

I'm curious, why not use a method?

const myObj = {
    assign(field, value) {
        return new Promise(resolve => {
            setTimeout( () => {
                myObj[field] = value;
		        resolve(value);
	        }, 100);
        });
    }
};

await myObj.assign("foo", 1);
1 Like

Uh, being able to use a promise-returning method in a property descriptor doesn't mean that JS has "asynchronous getters". In fact { async get field() {…} } syntax is not allowed. Why not just use a normal method?

See also Async Getters and Setters. Is it Possible? | by David Barral | Trabe | Medium or ecmascript 6 - (ES6) class (ES2017) async / await getter - Stack Overflow.

Hi! I'm very sorry for late answer, was extremely busy.

@aclaymore
Totally agree with curiosity of using instance method, behaviour itself is absolutely the same, though just one thing is different -- here we Invoke that method instead of just passing argument through assignment process.
And that is why I'm not so agree with Reflect.set:

  • from the first sight Reflect is good and we are able to see if Assignment is completed
  • however the same time we must use Reflect API every time we want the result to be presented
  • in my code-piece it is obvious we will always have true value to be returned as a result of Assignment operation, where I want the other behaviour, I want Promise to be returned as a value of assignment instead of just fact we made that assignment correctly

and in a continuation to that last point

@bergus
you are totally right async get syntax is not allowed when you are trying to describe instance method due to the Class declaration or just making some object fields. But when we are using Object Property Descriptor API, then we are able to use async get. I don't know why, may be it is just V8 implementation error.

I just meant the code-piece is definitely working code for latest V8 engine:

Object.defineProperty( myObj, 'field', {
	async get () {
		await // something
	},
	async set ( value ) {
		await // something
	}
} );

If you want proof of concept just copy and paste this tread code from the initial message to node.js REPL or Chrome/Chromium or Firefox console.

And my point is if we may use it for some continuation tracking~tracing
And for Async Getters it is totally OK, I just have to await obj.field when I want the value from that field to be returned correctly. But for Async Setters there is nothing to await, cause undefined value returned immediately instead of Promise as a result of Assignment Operation.

And I want the behaviour to be the following:
If we have async set made through Object.defineProperty then instead of undefined we may return that Promise useing await obj.field = value syntax. If we will use Reflect API then instead of true value we will receive Promise object, which is obviously truthly value using non strict comparison. So the Reflect API may remain it's own behaviour unchanged somehow, while we will be able to use that promise for reason of tracking if is rejected or fulfilled.

const promiseResult = await obj.field = value

or

const promiseResult = (await obj.field = value)

or

const promiseResult = await (obj.field = value)

or, for Reflection

const promiseResult = await Reflect.set(obj, "field", value)

or


try {
    await obj.field = value
} catch (awaitError) {
    console.error(`
      The result of Async Assignmet is failed for reason of :
    `, awaitError);
}

or

process.on('unhandledRejection', (reason, promise) => {
   // and here promise is that invoked by async set 
}

Finally, it is up to us to prohibit of that async get and async set abilities through .definePropery API... So may be that is the point of where we have to transform this proposal.

Though I think it is just beautiful thing: if somebody really need Async Setter, they have to use .defineProperty API for that, and then they will be able to unlock that achievement.

It's not an error, and it works in any engine not just V8. It's just not related to getters/setters at all, it isn't special syntax. It's just an async method declaration in an object literal, with the property names get and set respectively.
Same as

const descriptor = {
	get: async function() {
		await // something
	},
	set: async function(value) {
		await // something
	},
};
Object.defineProperty(myObj, 'field', descriptor);

@bergus thank you!

Exactly that is why when I thought of how to make async getters and setters I understood we may find necessary behaviour via .defineProperty API, cause there is no restriction.
And that is why I tried to use it, and obviously it's working.

And then I realised I may use await inside of getter, and it will work as I expect, cause promise chain will keep continuation till the final value, and it was what I wanted.

And while using async keyword for Setters it is the same, just I have no place to put await for it. So I cannot track the moment when that Async Operation inside of Setter is accomplished. And moreover I cannot track if there will be that setter promise error, unless I use process.on('unhandledRejection' for that. And finally aware of using use try~catch for co-invented so-called "async setter".

So, I don't want Assignment Operation itself become Asynchronous. I just want that Promise of async set become useful somehow, so I'd like to handle that promise via simple syntax in addition to that we may use await syntax inside of setter.

Hi!
I'm interested if we may continue this discussion or I have to create more direct proposal for that exact final thing: ability to handle promise from async set instruction during assignment operation ? @bergus @aclaymore what do you think ?

Are there examples of other languages that have this feature?

@aclaymore I don't know if there are any prior art

I would be against any such proposal. I think that assignment should never return anything other than the rhs value, the = operator should not become overloaded by a setter on the lhs. Just use a normal method and await it, there's no need for syntactic sugar.

@bergus very good point !

totally agree, we figured out there is no necessity for change

this is also absolutely ok, and no need for changes

and I'm also personally agree with that

the only point why I still insist on discussion is that there is no syntax for handling promise returned by invocation of async set declaration. And therefore at first it will anyway jump to process.on('unhandledRejection' in case of error. In second we are missing the ability to retrieve value from that promise in case of correct resolve operation. Though, anyway we are able to try~catch whole code inside of that async set declaration, and this is also ok.