Maison > interface Web > js tutoriel > le corps du texte

Explication détaillée de la nouvelle fonctionnalité Itération asynchrone dans ES9

青灯夜游
Libérer: 2021-04-20 09:49:06
avant
2467 Les gens l'ont consulté

Explication détaillée de la nouvelle fonctionnalité Itération asynchrone dans ES9

Dans ES6, le concept d'itération synchrone a été introduit avec la référence de l'opérateur Async dans ES8, peut-il être parcouru dans une opération asynchrone ?

Aujourd'hui, je vais vous parler de la nouvelle fonctionnalité de parcours asynchrone dans ES9, l'itération Async.

Parcours asynchrone


Avant d'expliquer le parcours asynchrone, rappelons d'abord le parcours synchrone dans ES6.

Selon la définition d'ES6, l'itération se compose principalement de trois parties :

1 Itérable

Regardons d'abord la définition. of Iterable :

interface Iterable {
    [Symbol.iterator]() : Iterator;
}
Copier après la connexion

Iterable signifie qu'il y a des données traversables dans cet objet et qu'une méthode d'usine qui peut générer un itérateur doit être implémentée.

2. Iterator

interface Iterator {
    next() : IteratorResult;
}
Copier après la connexion

Iterator peut être construit à partir d'Iterable. Iterator est un concept semblable à un curseur, et IteratorResult est accessible via next.

3. IteratorResult

IteratorResult sont les données obtenues à chaque appel de la méthode suivante.

interface IteratorResult {
    value: any;
    done: boolean;
}
Copier après la connexion

En plus d'une valeur indiquant les données à obtenir, IteratorResult a également un done indiquant si le parcours est terminé.

Ce qui suit est un exemple de parcours d'un tableau :

> 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 }
Copier après la connexion

Mais l'exemple ci-dessus traverse des données synchrones, si nous obtenons des données asynchrones, comme un fichier téléchargé depuis l'extrémité http, nous voulons. Il faut parcourir le fichier ligne par ligne. La lecture d’une ligne de données étant une opération asynchrone, cela implique un parcours de données asynchrone.

La méthode pour ajouter une lecture asynchrone des fichiers est readLinesFromFile, donc la méthode de traversée synchrone n'est plus applicable à l'asynchrone :

//不再适用
for (const line of readLinesFromFile(fileName)) {
    console.log(line);
}
Copier après la connexion

Peut-être penserez-vous, pouvons-nous utiliser l'asynchrone. Que diriez-vous d'encapsuler l'opération de lire une ligne dans une promesse puis de la parcourir de manière synchrone ?

L'idée est bonne, mais dans ce cas, il est impossible de détecter si l'opération asynchrone est terminée. La méthode n’est donc pas réalisable.

ES9 a donc introduit le concept de parcours asynchrone :

  • L'itérateur dans les itérables asynchrones peut être obtenu via Symbol.asyncIterator.

  • La méthode next() de l'itérateur asynchrone renvoie un objet Promises, qui contient IteratorResults.

Alors, regardons la définition API du parcours asynchrone :

interface AsyncIterable {
    [Symbol.asyncIterator]() : AsyncIterator;
}
interface AsyncIterator {
    next() : Promise<IteratorResult>;
}
interface IteratorResult {
    value: any;
    done: boolean;
}
Copier après la connexion

Regardons une application de parcours asynchrone :

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 }
});
Copier après la connexion

où createAsyncIterable sera A L'itérable synchrone est converti en un itérable asynchrone. Nous verrons comment il est généré dans la section suivante.

Ici, nous nous concentrons principalement sur l'opération de traversée d'asyncIterator.

Étant donné que l'opérateur Async a été introduit dans ES8, nous pouvons également réécrire le code ci-dessus à l'aide de la fonction 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 }
}
Copier après la connexion

Parcours itérable asynchrone


Utilisez for-of pour parcourir un itérable synchrone et utilisez for-wait-of pour parcourir un itérable asynchrone.

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

Notez que wait doit être placé dans la fonction async.

Si une exception se produit pendant notre parcours asynchrone, vous pouvez utiliser try catch in for-await-of pour intercepter l'exception :

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!
    }
})();
Copier après la connexion

L'itérable synchronisé renvoie les itérateurs synchrones, next La méthode renvoie {value , fait}.

Si vous utilisez for-await-of, les itérateurs synchrones seront convertis en itérateurs asynchrones. La valeur renvoyée est ensuite convertie en promesse.

Si la valeur renvoyée par synchronous next lui-même est un objet Promise, la valeur de retour asynchrone est toujours la même promesse.

C'est-à-dire qu'il convertira : Iterable<Promise<T>> en AsyncIterable<T>, comme le montre l'exemple suivant :

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
Copier après la connexion

L'exemple ci-dessus convertit une promesse synchrone en une promesse asynchrone.

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

// Output:
// c
// d
Copier après la connexion

L'exemple ci-dessus convertit les constantes synchronisées en Promise. On voit que les résultats des deux sont les mêmes.

Génération d'itérable asynchrone


Retour à l'exemple ci-dessus, nous utilisons createAsyncIterable(syncIterable) pour convertir syncIterable en AsyncIterable.

Voyons comment cette méthode est implémentée :

async function* createAsyncIterable(syncIterable) {
    for (const elem of syncIterable) {
        yield elem;
    }
}
Copier après la connexion

Dans le code ci-dessus, nous ajoutons async devant une fonction de générateur ordinaire, qui représente un générateur asynchrone.

Pour les générateurs ordinaires, chaque fois que la méthode suivante est appelée, un objet {value,done} sera renvoyé Cet objet objet est une encapsulation de la valeur de rendement.

Pour un générateur asynchrone, chaque fois que la méthode suivante est appelée, un objet de promesse contenant l'objet {value,done} sera renvoyé. Cet objet objet est une encapsulation de la valeur de rendement.

Étant donné qu'un objet Promise est renvoyé, nous n'avons pas besoin d'attendre la fin du résultat de l'exécution asynchrone avant d'appeler à nouveau la méthode suivante.

Nous pouvons utiliser un Promise.all pour effectuer toutes les opérations Promise asynchrones en même temps :

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
Copier après la connexion

Dans createAsyncIterable, nous créons un Iterable asynchrone à partir d'un Iterable synchrone.

Voyons ensuite comment créer un Iterable asynchrone à partir d'un Iterable asynchrone.

De la section précédente, nous savons que vous pouvez utiliser for-await-of pour lire des données Itérables asynchrones, nous pouvons donc l'utiliser comme ceci :

async function* prefixLines(asyncIterable) {
    for await (const line of asyncIterable) {
        yield &#39;> &#39; + line;
    }
}
Copier après la connexion

在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
Copier après la connexion

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

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

异步方法和异步生成器


异步方法是使用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!
Copier après la connexion

异步生成器是使用 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 }
Copier après la connexion

本文作者:flydean程序那些事

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

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:flydean的博客
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal