使用 javascript 的 Symbol.asyncIterator 和 for await of loop
Using javascript's Symbol.asyncIterator with for await of loop
我正在尝试理解 javascript 的 Symbol.asyncIterator and for await of。我写了一些简单的代码,它抛出一个错误:
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
的函数,该函数 return 是一个 Promise
.然后我使用 for await of
循环遍历函数将 return.
的所有值
我做错了什么?
PS:我在 Node 版本 10.13.0
和最新版本 Chrome
您应该使 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
没有 [=20] =] 任何地方,否则 x
将是一个解析为值的 Promise,而不是值本身。
yield
ing Promise.resolve
inside an async generator is odd, though - unless you want 结果是 Promise(这需要额外的await
在 for await
循环内),在 async
生成器内 await
更有意义,然后 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
将不得不 return 一个 iterator(具有 value
属性 和一个 next
函数)。
test
函数不能 return 一个 promise,而是一个迭代器(一个带有 next()
的对象)方法,然后该方法必须 return 一个 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})`;
}
}
}
要成为有效的 asyncIterator
,您的 test
函数必须 return 具有 next
方法的对象 return 是结果对象的承诺具有 value
和 done
属性。 (从技术上讲,如果 value
的值为 undefined
,则 value
是可选的;如果其值为 false
,则 done
是可选的,但是...)
您可以通过几种方式做到这一点:
- 完全手动(笨拙,特别是如果你想要正确的原型)
- 半手动(稍微不那么尴尬,但仍然很难获得正确的原型)
- 使用异步生成器函数(最简单)
您完全可以手动完成(这不会尝试获得正确的原型):
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))
您可以半手动编写一个函数,该函数 return 是一个具有 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 运行时本身获得的所有异步迭代器都继承自原型,该原型提供确保迭代器也是 iterable 的非常基本的功能(通过让 Symbol.iterator
成为函数 returning this
)。该原型没有公开可用的标识符或 属性,您必须跳过箍才能获得它:
const asyncIteratorPrototype =
Object.getPrototypeOf(
Object.getPrototypeOf(
async function*(){}.prototype
)
);
然后您将使用它作为对象的原型以及您正在 returning:
的 next
方法
return Object.assign(Object.create(asyncIteratorPrototype), {
next() {
// ...
}
});
我正在尝试理解 javascript 的 Symbol.asyncIterator and for await of。我写了一些简单的代码,它抛出一个错误:
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
的函数,该函数 return 是一个 Promise
.然后我使用 for await of
循环遍历函数将 return.
我做错了什么?
PS:我在 Node 版本 10.13.0
和最新版本 Chrome
您应该使 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
没有 [=20] =] 任何地方,否则 x
将是一个解析为值的 Promise,而不是值本身。
yield
ing Promise.resolve
inside an async generator is odd, though - unless you want 结果是 Promise(这需要额外的await
在 for await
循环内),在 async
生成器内 await
更有意义,然后 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
将不得不 return 一个 iterator(具有 value
属性 和一个 next
函数)。
test
函数不能 return 一个 promise,而是一个迭代器(一个带有 next()
的对象)方法,然后该方法必须 return 一个 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})`;
}
}
}
要成为有效的 asyncIterator
,您的 test
函数必须 return 具有 next
方法的对象 return 是结果对象的承诺具有 value
和 done
属性。 (从技术上讲,如果 value
的值为 undefined
,则 value
是可选的;如果其值为 false
,则 done
是可选的,但是...)
您可以通过几种方式做到这一点:
- 完全手动(笨拙,特别是如果你想要正确的原型)
- 半手动(稍微不那么尴尬,但仍然很难获得正确的原型)
- 使用异步生成器函数(最简单)
您完全可以手动完成(这不会尝试获得正确的原型):
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))
您可以半手动编写一个函数,该函数 return 是一个具有 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 运行时本身获得的所有异步迭代器都继承自原型,该原型提供确保迭代器也是 iterable 的非常基本的功能(通过让 Symbol.iterator
成为函数 returning this
)。该原型没有公开可用的标识符或 属性,您必须跳过箍才能获得它:
const asyncIteratorPrototype =
Object.getPrototypeOf(
Object.getPrototypeOf(
async function*(){}.prototype
)
);
然后您将使用它作为对象的原型以及您正在 returning:
的next
方法
return Object.assign(Object.create(asyncIteratorPrototype), {
next() {
// ...
}
});