Closed rbuckton closed 1 year ago
It says await using
, how is that not an explicit await?
@ljharb if the resource implements Symbol.dispose
returning a promise, it is not an async resource. An async resource is an object implementing Symbol.asyncDispose
.
The precedent here is for await (const foo of syncIterable)
. In that case I believe the async iterator wrapper does adopt a promise returned by the sync iterable.
Whether to adopt that precedent or not is the question. It really begs the question of why a sync resource might return a promise from its Symbol.dispose
method in the first place.
I agree that would be strange, but I don't see a strong argument to deviate from precedent. If I await a non-promise object it's still awaited.
@ljharb if the resource implements
Symbol.dispose
returning a promise, it is not an async resource. An async resource is an object implementingSymbol.asyncDispose
.The precedent here is
for await (const foo of syncIterable)
. In that case I believe the async iterator wrapper does adopt a promise returned by the sync iterable.Whether to adopt that precedent or not is the question. It really begs the question of why a sync resource might return a promise from its
Symbol.dispose
method in the first place.
for-await
is a bit complicated, but there are some parts of the Iterator protocol that are not awaited as part of AsyncFromSyncIterator, such as the call to next()
. As a result, the precedent isn't actually very clear.
I agree that it should not await, as I suggested in https://github.com/tc39/proposal-async-explicit-resource-management/issues/17.
Conceptually, the default implementation of [@@asyncDispose]
should be
[@@asyncDispose]() {
this[@@dispose]();
}
and not
[@@asyncDispose]() {
return this[@@dispose]();
}
given that there is no expected return value.
My expectation would be to not await:
const o = {
[Symbol.asyncIterator]() {
return async function* () {
for(let i = 0; i < 10; i++) yield i;
}();
}
}
let breaker = 0;
for await (const item of o) {
console.log(item);
if(breaker++>100) break;
}
This logs 0..9 but:
const o = {
[Symbol.iterator]() {
return async function* () {
for(let i = 0; i < 10; i++) yield i;
}();
}
}
let breaker = 0;
for await (const item of o) {
console.log(item);
if(breaker++>100) break;
}
This logs undefined 100 times.
It would be weird for await using
to await the return value from a user expectation and language consistency PoV in my opinion.
While we intend to maintain the consensus that an evaluated
await using
will always imply an implicitAwait
at the end of the block, it was suggested that the return value of a synchronous[Symbol.dispose]()
method shouldn't itself beAwait
-ed, even if that method returns aPromise
so as to remain consistent with the synchronoususing
statement.The following is are examples of the current behavior and the suggested behavior:
Current behavior
Suggested behavior