ホームページ  >  記事  >  ウェブフロントエンド  >  ES9 の新機能非同期イテレーションの詳細な説明

ES9 の新機能非同期イテレーションの詳細な説明

青灯夜游
青灯夜游転載
2021-04-20 09:49:062441ブラウズ

ES9 の新機能非同期イテレーションの詳細な説明

ES6 では、同期反復の概念が導入されましたが、ES8 では Async オペレーターを参照することで、非同期操作でトラバーサル操作を実行できますか?

今日は、ES9 の非同期トラバーサルの新機能である非同期反復についてお話したいと思います。

非同期トラバーサル


非同期トラバーサルを説明する前に、まず ES6 の同期トラバーサルを思い出してください。

ES6 の定義によると、反復は主に 3 つの部分で構成されます:

1. Iterable

まず定義を見てみましょう。 of Iterable:

interface Iterable {
    [Symbol.iterator]() : Iterator;
}

Iterable は、このオブジェクト内に走査可能なデータがあり、Iterator を生成できるファクトリ メソッドを実装する必要があることを意味します。

2. Iterator

interface Iterator {
    next() : IteratorResult;
}

Iterator は Iterable から構築できます。 Iterator はカーソルのような概念であり、IteratorResult には next を通じてアクセスできます。

3. IteratorResult

IteratorResult は、次のメソッドが呼び出されるたびに取得されるデータです。

interface IteratorResult {
    value: any;
    done: boolean;
}

IteratorResult には、取得するデータを示す値に加えて、トラバーサルが完了したかどうかを示す Done も含まれます。

次は配列を走査する例です:

> const iterable = ['a', 'b'];
> const iterator = iterable[Symbol.iterator]();
> iterator.next()
{ value: 'a', done: false }
> iterator.next()
{ value: 'b', done: false }
> iterator.next()
{ value: undefined, done: true }

ただし、上記の例は同期データを走査します。http 側からダウンロードされたファイルなどの非同期データを取得する場合は、次のようにします。ファイルを 1 行ずつ移動します。データ行の読み取りは非同期操作であるため、これには非同期データ トラバーサルが含まれます。

ファイルの非同期読み取りを追加するメソッドは readLinesFromFile であるため、同期トラバーサル メソッドは非同期には適用できなくなりました:

//不再适用
for (const line of readLinesFromFile(fileName)) {
    console.log(line);
}

おそらく、非同期を使用できるのではないかと思われるでしょう。操作をカプセル化してはどうでしょうかPromise 内の行を読み取り、それを同期的に走査するのはどうでしょうか?

アイデアは良いのですが、この場合、非同期操作が完了したかどうかを検出することはできません。したがって、その方法は実現不可能です。

そこで、ES9 では非同期トラバーサルの概念が導入されました。

  • 非同期イテラブル内のイテレータは、Symbol.asyncIterator を通じて取得できます。

  • 非同期イテレータの next() メソッドは、IteratorResults を含む Promises オブジェクトを返します。

それでは、非同期トラバーサルの API 定義を見てみましょう:

interface AsyncIterable {
    [Symbol.asyncIterator]() : AsyncIterator;
}
interface AsyncIterator {
    next() : Promise<IteratorResult>;
}
interface IteratorResult {
    value: any;
    done: boolean;
}

非同期トラバーサル アプリケーションを見てみましょう:

const asyncIterable = createAsyncIterable([&#39;a&#39;, &#39;b&#39;]);
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
asyncIterator.next()
.then(iterResult1 => {
    console.log(iterResult1); // { value: &#39;a&#39;, done: false }
    return asyncIterator.next();
})
.then(iterResult2 => {
    console.log(iterResult2); // { value: &#39;b&#39;, done: false }
    return asyncIterator.next();
})
.then(iterResult3 => {
    console.log(iterResult3); // { value: undefined, done: true }
});

ここで、createAsyncIterable は A同期反復可能は非同期反復可能に変換されますが、次のセクションでその生成方法を見ていきます。

ここでは主に asyncIterator のトラバーサル操作に焦点を当てます。

Async オペレーターは ES8 で導入されたため、Async 関数を使用して上記のコードを書き直すこともできます。

async function f() {
    const asyncIterable = createAsyncIterable([&#39;a&#39;, &#39;b&#39;]);
    const asyncIterator = asyncIterable[Symbol.asyncIterator]();
    console.log(await asyncIterator.next());
        // { value: &#39;a&#39;, done: false }
    console.log(await asyncIterator.next());
        // { value: &#39;b&#39;, done: false }
    console.log(await asyncIterator.next());
        // { value: undefined, done: true }
}

Asynchronous iterable traversal


同期反復可能を横断するには for-of を使用し、非同期反復可能を横断するには for-await-of を使用します。

async function f() {
    for await (const x of createAsyncIterable([&#39;a&#39;, &#39;b&#39;])) {
        console.log(x);
    }
}
// Output:
// a
// b

await は async 関数内に配置する必要があることに注意してください。

非同期トラバーサル中に例外が発生した場合は、for-await-of で try catch を使用して例外をキャッチできます。

function createRejectingIterable() {
    return {
        [Symbol.asyncIterator]() {
            return this;
        },
        next() {
            return Promise.reject(new Error(&#39;Problem!&#39;));
        },
    };
}
(async function () { 
    try {
        for await (const x of createRejectingIterable()) {
            console.log(x);
        }
    } catch (e) {
        console.error(e);
            // Error: Problem!
    }
})();

Synchronized iterable returns synchronous iterators, next メソッドは {value を返します、 終わり}。

for-await-of を使用すると、同期反復子は非同期反復子に変換されます。返された値は Promise に変換されます。

同期された next 自体によって返される値が Promise オブジェクトである場合、非同期の戻り値は依然として同じ Promise です。

つまり、次の例に示すように、Iterableea14b2e011575f0c7465d02dc55e095c>AsyncIterable8742468051c85b06f0a0af9e3e506b5c に変換します。 #

async function main() {
    const syncIterable = [
        Promise.resolve(&#39;a&#39;),
        Promise.resolve(&#39;b&#39;),
    ];
    for await (const x of syncIterable) {
        console.log(x);
    }
}
main();

// Output:
// a
// b

上記の例は、同期 Promise を非同期 Promise に変換します。

async function main() {
    for await (const x of [&#39;a&#39;, &#39;b&#39;]) {
        console.log(x);
    }
}
main();

// Output:
// c
// d

上記の例では、同期定数を Promise に変換します。どちらの結果も同じであることがわかります。

非同期 iterable の生成


上記の例に戻り、createAsyncIterable(syncIterable) を使用して syncIterable を AsyncIterable に変換します。


このメソッドがどのように実装されるかを見てみましょう:

async function* createAsyncIterable(syncIterable) {
    for (const elem of syncIterable) {
        yield elem;
    }
}

上記のコードでは、非同期ジェネレーターを表す通常のジェネレーター関数の前に async を追加します。

通常のジェネレーターの場合、next メソッドが呼び出されるたびに、オブジェクト {value,done} が返され、このオブジェクト オブジェクトは、yield 値をカプセル化します。

非同期ジェネレーターの場合、次のメソッドが呼び出されるたびに、オブジェクト {value,done} を含む Promise オブジェクトが返されます。このオブジェクト object は、yield 値のカプセル化です。

Promise オブジェクトが返されるため、次のメソッドを再度呼び出す前に、非同期実行の結果が完了するまで待つ必要はありません。

Promise.all を使用すると、すべての非同期 Promise 操作を同時に実行できます。

const asyncGenObj = createAsyncIterable([&#39;a&#39;, &#39;b&#39;]);
const [{value:v1},{value:v2}] = await Promise.all([
    asyncGenObj.next(), asyncGenObj.next()
]);
console.log(v1, v2); // a b

createAsyncIterable では、同期 Iterable から非同期 Iterable を作成します。

次に、非同期 Iterable から非同期 Iterable を作成する方法を見てみましょう。

前のセクションで、for-await-of を使用して非同期 Iterable データを読み取ることができることがわかったので、次のように使用できます。
async function* prefixLines(asyncIterable) {
    for await (const line of asyncIterable) {
        yield &#39;> &#39; + line;
    }
}

在generator一文中,我们讲到了在generator中调用generator。也就是在一个生产器中通过使用yield*来调用另外一个生成器。

同样的,如果是在异步生成器中,我们可以做同样的事情:

async function* gen1() {
    yield &#39;a&#39;;
    yield &#39;b&#39;;
    return 2;
}
async function* gen2() {
    const result = yield* gen1(); 
        // result === 2
}

(async function () {
    for await (const x of gen2()) {
        console.log(x);
    }
})();
// Output:
// a
// b

如果在异步生成器中抛出异常,这个异常也会被封装在Promise中:

async function* asyncGenerator() {
    throw new Error(&#39;Problem!&#39;);
}
asyncGenerator().next()
.catch(err => console.log(err)); // Error: Problem!

异步方法和异步生成器


异步方法是使用async function 声明的方法,它会返回一个Promise对象。

function中的return或throw异常会作为返回的Promise中的value。

(async function () {
    return &#39;hello&#39;;
})()
.then(x => console.log(x)); // hello

(async function () {
    throw new Error(&#39;Problem!&#39;);
})()
.catch(x => console.error(x)); // Error: Problem!

异步生成器是使用 async function * 申明的方法。它会返回一个异步的iterable。

通过调用iterable的next方法,将会返回一个Promise。异步生成器中yield 的值会用来填充Promise的值。如果在生成器中抛出了异常,同样会被Promise捕获到。

async function* gen() {
    yield &#39;hello&#39;;
}
const genObj = gen();
genObj.next().then(x => console.log(x));
    // { value: &#39;hello&#39;, done: false }

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/es9-async-iteration/

更多编程相关知识,请访问:编程视频!!

以上がES9 の新機能非同期イテレーションの詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はflydean的博客で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。