-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Using for-await-of should resolve the Promise? #21115
Comments
for (const item of arrayOfPromises) {
console.log(await item);
} or: |
@andy-ms An array of promises should work. The The What you can't give as an argument to |
OK, based on |
Thanks, so how to fix this? Does simply changing /src/lib/esnext.asynciterable.d.ts#L14 |
I think we need to ensure the emit would work with that as well. CC @rbuckton |
Just as a note Chrome's semantics are correct in regards to the current spec so you can try things out in there. Here's some sample outputs that can happen: Iterating an async generator: async function* gen() {
yield 1
yield new Promise(resolve => setTimeout(resolve, 1000, 2)
yield 3
}
for await (const item of gen()) {
console.log(item)
}
// -> 1
// -> 2 // After 1 second
// -> 3 // Also after 1 second Iterating an iterable: function* gen() {
yield 1
yield new Promise(resolve => setTimeout(resolve, 1000, 2)
yield 3
}
for await (const item of gen()) {
console.log(item)
}
// Exactly the same as above
// -> 1
// -> 2
// -> 3 Iterating an async iterable that actually emits Promises in the const values = [
_ => 1,
_ => new Promise(resolve => setTimeout(resolve, 1000, 2)),
_ => Promise.resolve(3)
]
const iterator = {
i: 0,
async next() {
if (this.i >= 3) {
return { done: true }
}
const value = values[this.i]()
this.i += 1
return { value, done: false }
},
[Symbol.asyncIterator]() {
return this
}
}
for await (const item of iterator) {
console.log(item)
}
// -> 1
// -> Promise{<pending>}
// -> Promise{<resolved>: 3} Iterating an async iterable that doesn't give actual Promises from // Same as previous but .next is not asynchronous
const values = [
_ => 1,
_ => new Promise(resolve => setTimeout(resolve, 1000, 2)),
_ => Promise.resolve(3)
]
const iterator = {
i: 0,
next() {
if (this.i >= 3) {
return { done: true }
}
const value = values[this.i]()
this.i += 1
return { value, done: false }
},
[Symbol.asyncIterator]() {
return this
}
}
for await (const item of iterator) {
console.log(item)
}
// Same as above
// -> 1
// -> Promise{<pending>}
// -> Promise{<resolved>: 3} Note that is even more confusing in the presence of const values = [
_ => 1,
_ => new Promise(resolve => setTimeout(resolve, 1000, 2)),
_ => Promise.resolve(3)
]
const iterator = {
i: 0,
next() {
if (this.i >= 3) {
return { done: true }
}
const value = values[this.i]()
this.i += 1
return { value, done: false }
},
[Symbol.asyncIterator]() {
return this
}
}
async function* it() {
yield* iterator
}
for await (const item of it()) {
console.log(item)
}
// -> 1
// -> 2 // After 1 second
// -> 3 // Also after 1 second With these outputs in mind there's two things to note, one is that a
Also whether or not allowing |
So for now I have: // Things you can "for await of" on
type ForAwaitOfable<T> =
| ReadonlyArray<T>
// | ReadonlyArray<Promise<T>> // https://github.com/Microsoft/TypeScript/issues/21115
| Iterable<T>
// | Iterable<Promise<T>>
| AsyncIterable<T>
; Am I missing anything? If emit is an issue then I would still like the typing change, and pass-through when target is |
We will need to verify the emit behavior against the final version of the async iteration spec and the related test-262 tests. |
I've looked into this more and it seems the only issue as of TypeScript 2.9 is the type we report. The value at runtime is correct.. |
So, does this const ar = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)];
async function main() {
for await (const a of ar) {
console.log(a);
}
} correctly translates to var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator];
return m ? m.call(o) : typeof __values === "function" ? __values(o) : o[Symbol.iterator]();
};
const ar = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)];
async function main() {
try {
for (var ar_1 = __asyncValues(ar), ar_1_1; ar_1_1 = await ar_1.next(), !ar_1_1.done;) {
const a = await ar_1_1.value;
console.log(a);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (ar_1_1 && !ar_1_1.done && (_a = ar_1.return)) await _a.call(ar_1);
}
finally { if (e_1) throw e_1.error; }
}
var e_1, _a;
} in Javascript? And does simply changing /src/lib/esnext.asynciterable.d.ts#L14 |
@rbuckton @rilut No it's still not correct, I tested in the latest version in the git repository and this example is still wrong: const values = [
_ => 1,
_ => new Promise(resolve => setTimeout(resolve, 1000, 2)),
_ => Promise.resolve(3),
];
const iterator = {
i: 0,
async next() {
if (this.i >= 3) {
return { done: true };
}
const value = values[this.i]();
this.i += 1;
return { value, done: false };
},
[Symbol.asyncIterator]() {
return this;
}
}
async function main() {
for await (const item of iterator) {
console.log(item);
}
}
main();
// -> 1
// -> Promise{<pending>}
// -> Promise{<resolved>: 3} Currently TypeScript still just prints out the numbers:
Which is not correct according to the spec, the correct behaviour should be exactly as in the comment above. You can even try this out now in Node 10 or Chrome both with no flags both of which have the correct output based on the specification. @rilut A more correct version would be this where function __asyncValues() {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator];
// Where AsyncFromSyncIterator implements the .value await-ing logic
// like in the spec
return m ? m.call(o) : typeof __values === "function" ? __values(o) : new AsyncFromSyncIterator(o[Symbol.iterator]());
}
async function main() {
try {
for (var ar_1 = __asyncValues(ar), ar_1_1; ar_1_1 = await ar_1.next(), !ar_1_1.done;) {
// No await-ing the value
const a = ar_1_1.value;
console.log(a);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (ar_1_1 && !ar_1_1.done && (_a = ar_1.return)) await _a.call(ar_1);
}
finally { if (e_1) throw e_1.error; }
}
var e_1, _a;
} |
TypeScript Version:
Code
tsc --lib es2017,esnext.asynciterable,dom Untitled-1.ts && node Untitled-1.js
Actual behavior:
item
is a Promise in the for-await-of iterator blockDoing this causes error:
Expected behavior:
In the for-await-of iterator block,
item
should not be a Promise, because it's the actual value instead of a PromiseThe text was updated successfully, but these errors were encountered: