[英]Using javascript's Symbol.asyncIterator with for await of loop
I am trying to understand javascript's Symbol.asyncIterator and for await of . 我试图了解javascript的Symbol.asyncIterator并等待 。 I wrote some simple code and it throws an error saying: 我写了一些简单的代码,并抛出一个错误:
TypeError: undefined is not a function
on the line which tries to use for await (let x of a)
. 在尝试for await (let x of a)
。
I could not understand the reason for it. 我不明白原因。
let a = {}
function test() {
for(let i=0; i < 10; i++) {
if(i > 5) {
return Promise.resolve(`Greater than 5: (${i})`)
}else {
return Promise.resolve(`Less than 5: (${i})`)
}
}
}
a[Symbol.asyncIterator] = test;
async function main() {
for await (let x of a) { // LINE THAT THROWS AN ERROR
console.log(x)
}
}
main()
.then(r => console.log(r))
.catch(err => console.log(err))
I create an empty object a
and insert a key Symbol.asyncIterator
on the same object and assign it a function named test
that returns a Promise
. 我创建一个空对象a
并在同一对象上插入键Symbol.asyncIterator
,并将其分配给名为test
的函数,该函数返回Promise
。 Then I use for await of
loop to iterate over all the values that the function would return. 然后,我使用for await of
循环来迭代该函数将返回的所有值。
What am I doing incorrectly? 我做错了什么?
PS: I am on the Node version 10.13.0
and on the latest version of Chrome
PS:我在Node版本10.13.0
和最新版本的Chrome
To be a valid asyncIterator
, your test
function must return an object with a next
method that returns a promise of a result object with value
and done
properties. 要成为有效的asyncIterator
,您的test
函数必须使用next
方法返回一个对象,该方法将返回带有value
和done
属性的结果对象的承诺。 (Technically, value
is optional if its value would be undefined
and done
is optional if its value would be false
, but...) (从技术上讲, value
是可选的,如果它的值将undefined
和done
是可选的,如果它的值是false
,但是......)
You can do that in a few ways: 您可以通过以下几种方式做到这一点:
You can do it completely manually (this doesn't try to get the right prototype): 您可以完全手动完成此操作(这不会尝试获得正确的原型):
function test() { let i = -1; return { next() { ++i; if (i >= 10) { return Promise.resolve({ value: undefined, done: true }); } return Promise.resolve({ value: i > 5 ? `Greater than 5: (${i})` : `Less than 5: (${i})`, done: false }); } }; } let a = { [Symbol.asyncIterator]: test }; async function main() { for await (let x of a) { console.log(x) } } main() .then(r => console.log(r)) .catch(err => console.log(err))
You can do it half-manually writing a function that returns an object with an async
next
method (still doesn't try to get the right prototype): 您可以手动编写一个函数,该函数使用async
next
方法返回对象(仍然不会尝试获取正确的原型):
function test() { let i = -1; return { async next() { ++i; if (i >= 10) { return { value: undefined, done: true }; } return { value: i > 5 ? `Greater than 5: (${i})` : `Less than 5: (${i})`, done: false }; } }; } let a = { [Symbol.asyncIterator]: test }; async function main() { for await (let x of a) { console.log(x) } } main() .then(r => console.log(r)) .catch(err => console.log(err))
Or you can just use an async
generator function (easiest, and automatically gets the right prototype): 或者,您可以只使用async
生成器函数(最简单,并自动获取正确的原型):
async function* test() { for (let i = 0; i < 10; ++i) { yield i > 5 ? `Greater than 5: (${i})` : `Less than 5: (${i})`; } } let a = { [Symbol.asyncIterator]: test }; async function main() { for await (let x of a) { console.log(x) } } main() .then(r => console.log(r)) .catch(err => console.log(err))
About prototypes: All async iterators you get from the JavaScript runtime itself inherit from a prototype that provides the very basic feature of ensuring the iterator is also iterable (by having Symbol.iterator
be a function returning this
). 关于原型:从JavaScript运行时本身获取的所有异步迭代器都继承自原型,该原型提供了确保迭代器也可迭代的非常基本的功能(通过让Symbol.iterator
作为返回this
的函数)。 There's no publicly-available identifer or property for that prototype, you have to jump through hoops to get it: 该原型没有可公开获得的标识符或属性,您必须跳过所有步骤才能获得它:
const asyncIteratorPrototype =
Object.getPrototypeOf(
Object.getPrototypeOf(
async function*(){}.prototype
)
);
Then you'd use that as the prototype of the object with the next
method that you're returning: 然后,将其用作对象的原型,并返回next
方法:
return Object.assign(Object.create(asyncIteratorPrototype), {
next() {
// ...
}
});
The test
function must not return a promise, but an Iterator (an object with a next()
) method, that method then has to return a Promise (which makes it an async iterator) and that Promise has to resolve to an object containing a value
and a done
key: test
函数一定不能返回promise,而是一个迭代器(带有next()
的对象)方法,然后该方法必须返回一个Promise(使其成为异步迭代器),并且Promise必须解析为一个包含value
和done
密钥:
function test() {
return {
next() {
return Promise.resolve({ value: "test", done: false });
}
};
}
Now while that works, it is not that useful yet. 现在,尽管可行,但还没那么有用。 You could however create the same behaviour with an async generator function: 但是,您可以使用异步生成器函数创建相同的行为:
async function* test() {
await Promise.resolve();
yield "test";
}
Or in your case: 或您的情况:
async function* test() {
for(let i = 0; i < 10; i++) {
if(i > 5) {
await Promise.resolve();
yield `Greater than 5: (${i})`;
}else {
await Promise.resolve();
yield `Less than 5: (${i})`;
}
}
}
You should make test
an async
generator function instead, and yield
instead of return
: 你应该让test
的async
发电机的功能 ,而是和yield
,而不是return
:
let a = {} async function* test() { for(let i=0; i < 10; i++) { if(i > 5) { yield Promise.resolve(`Greater than 5: (${i})`) }else { yield Promise.resolve(`Less than 5: (${i})`) } } } a[Symbol.asyncIterator] = test; async function main() { for await (let x of a) { console.log(x) } } main() .then(r => console.log(r)) .catch(err => console.log(err))
It looks like the test
function needs to be async so that the x
in the for await
gets unwrapped, even though test
doesn't await
anywhere, otherwise the x
will be a Promise that resolves to the value, not the value itself. 看起来test
函数需要异步,以便for await
中的x
会被解包,即使test
不在任何地方都await
,否则x
将是一个解析为值而不是值本身的Promise。
yield
ing Promise.resolve
inside an async generator is odd, though - unless you want the result to be a Promise (which would require an extra await
inside the for await
loop), it'll make more sense to await
inside the async
generator, and then yield
the result. yield
荷兰国际集团Promise.resolve
异步发电机内是奇数,但-除非你想要的结果是一个承诺(这需要一个额外的await
里面for await
,它会更有意义的循环) await
内部async
发电机,然后yield
结果。
const delay = ms => new Promise(res => setTimeout(res, ms)); let a = {} async function* test() { for(let i=0; i < 10; i++) { await delay(500); if(i > 5) { yield `Greater than 5: (${i})`; }else { yield `Less than 5: (${i})`; } } } a[Symbol.asyncIterator] = test; async function main() { for await (let x of a) { console.log(x) } } main() .then(r => console.log(r)) .catch(err => console.log(err))
If you didn't make test
a generator, test
would have to return an iterator (an object with a value
property and a next
function). 如果您没有使test
成为生成器,则test
必须返回一个迭代器 (具有value
属性和next
函数的对象)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.