使用 javascript 的 Symbol.asyncIterator 和 for await of loop

Suh*_*pta 5 javascript node.js async-iterator ecmascript-2018

我正在尝试了解 javascript 的Symbol.asyncIteratorawait of。我写了一些简单的代码,它抛出一个错误说:

    TypeError: undefined is not a function
Run Code Online (Sandbox Code Playgroud)

在尝试使用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))
Run Code Online (Sandbox Code Playgroud)

我创建一个空对象aSymbol.asyncIterator在同一个对象上插入一个键,并为其分配一个名为的函数test,该函数返回一个Promise. 然后我使用for await of循环来迭代函数将返回的所有值。

我做错了什么?

PS:我使用的是 Node 版本10.13.0和最新版本的Chrome

T.J*_*der 7

要成为有效的asyncIterator,您的test函数必须返回一个对象,该对象的next方法返回结果对象的承诺,value并且done属性。(从技术上讲,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))
Run Code Online (Sandbox Code Playgroud)

您可以半手动编写一个函数,该函数返回一个带有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))
Run Code Online (Sandbox Code Playgroud)

或者你可以只使用一个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))
Run Code Online (Sandbox Code Playgroud)


关于原型:您从 JavaScript 运行时本身获得的所有异步迭代器都继承自一个原型,该原型提供了确保迭代器也是可迭代的非常基本的特性(通过Symbol.iterator成为一个返回 的函数this)。该原型没有公开可用的标识符或属性,您必须跳过箍才能获得它:

const asyncIteratorPrototype =
    Object.getPrototypeOf(
        Object.getPrototypeOf(
            async function*(){}.prototype
        )
    );
Run Code Online (Sandbox Code Playgroud)

然后,您将使用它作为next您返回的方法的对象的原型:

return Object.assign(Object.create(asyncIteratorPrototype), {
    next() {
        // ...
    }
});
Run Code Online (Sandbox Code Playgroud)

  • @SuhailGupta - 是的,生成器/异步生成器本身提供了包装结果对象,这就是为什么生成器对于创建迭代器如此方便的部分原因。使用`yield`,结果对象将具有产生的值和`done: false`。使用 `return`(在生成器函数中),结果对象将具有生成的值和 `done: true`。(类似地:`for-await-of` 和 `for-of` 使用结果对象,在幕后使用其 `value` 和 `next`。) (2认同)