繁体   English   中英

使用javascript的Symbol.asyncIterator与等待循环

[英]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方法返回一个对象,该方法将返回带有valuedone属性的结果对象的承诺。 (从技术上讲, value是可选的,如果它的值将undefineddone是可选的,如果它的值是false ,但是......)

您可以通过以下几种方式做到这一点:

  1. 完全手动(笨拙,特别是如果您想要合适的原型时)
  2. 半手动(稍微不那么尴尬,但仍然很难获得正确的原型)
  3. 使用异步生成器函数(最简单)

您可以完全手动完成此操作(这不会尝试获得正确的原型):

 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必须解析为一个包含valuedone密钥:

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})`;
    }
  }
}

你应该让testasync 发电机的功能 ,而是和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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM