[英]Using javascript's Symbol.asyncIterator with for await of loop
我试图了解javascript的Symbol.asyncIterator并等待 。 我写了一些简单的代码,并抛出一个错误:
TypeError: undefined is not a function
在尝试for await (let x of a)
。
我不明白原因。
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))
我创建一个空对象a
并在同一对象上插入键Symbol.asyncIterator
,并将其分配给名为test
的函数,该函数返回Promise
。 然后,我使用for await of
循环来迭代该函数将返回的所有值。
我做错了什么?
PS:我在Node版本10.13.0
和最新版本的Chrome
要成为有效的asyncIterator
,您的test
函数必须使用next
方法返回一个对象,该方法将返回带有value
和done
属性的结果对象的承诺。 (从技术上讲, value
是可选的,如果它的值将undefined
和done
是可选的,如果它的值是false
,但是......)
您可以通过以下几种方式做到这一点:
您可以完全手动完成此操作(这不会尝试获得正确的原型):
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))
您可以手动编写一个函数,该函数使用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))
或者,您可以只使用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))
关于原型:从JavaScript运行时本身获取的所有异步迭代器都继承自原型,该原型提供了确保迭代器也可迭代的非常基本的功能(通过让Symbol.iterator
作为返回this
的函数)。 该原型没有可公开获得的标识符或属性,您必须跳过所有步骤才能获得它:
const asyncIteratorPrototype =
Object.getPrototypeOf(
Object.getPrototypeOf(
async function*(){}.prototype
)
);
然后,将其用作对象的原型,并返回next
方法:
return Object.assign(Object.create(asyncIteratorPrototype), {
next() {
// ...
}
});
test
函数一定不能返回promise,而是一个迭代器(带有next()
的对象)方法,然后该方法必须返回一个Promise(使其成为异步迭代器),并且Promise必须解析为一个包含value
和done
密钥:
function test() {
return {
next() {
return Promise.resolve({ value: "test", done: false });
}
};
}
现在,尽管可行,但还没那么有用。 但是,您可以使用异步生成器函数创建相同的行为:
async function* test() {
await Promise.resolve();
yield "test";
}
或您的情况:
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})`;
}
}
}
你应该让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))
看起来test
函数需要异步,以便for await
中的x
会被解包,即使test
不在任何地方都await
,否则x
将是一个解析为值而不是值本身的Promise。
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))
如果您没有使test
成为生成器,则test
必须返回一个迭代器 (具有value
属性和next
函数的对象)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.