>  기사  >  웹 프론트엔드  >  ES9의 새로운 기능 비동기 반복에 대한 자세한 설명

ES9의 새로운 기능 비동기 반복에 대한 자세한 설명

青灯夜游
青灯夜游앞으로
2021-04-20 09:49:062441검색

ES9의 새로운 기능 비동기 반복에 대한 자세한 설명

ES6에서는 동기 반복 개념이 도입되었습니다. ES8의 Async 연산자를 참조하여 비동기 작업에서 순회할 수 있나요?

오늘 저는 ES9의 비동기 탐색의 새로운 기능인 Async iteration에 대해 말씀드리고 싶습니다.

비동기 순회


비동기 순회를 설명하기 전에 먼저 ES6의 동기 순회를 떠올려 보겠습니다.

ES6의 정의에 따르면 반복은 주로 세 부분으로 구성됩니다.

1. Iterable

먼저 Iterable의 정의를 살펴보겠습니다.

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

Iterable은 이 객체에 탐색 가능한 데이터가 있음을 의미합니다. Iterator를 생성하려면 Factory 메소드를 구현해야 합니다.

2. Iterator

interface Iterator {
    next() : IteratorResult;
}

Iterator는 Iterable에서 빌드할 수 있습니다. Iterator는 커서와 유사한 개념으로, IteratorResult는 next를 통해 접근할 수 있습니다.

3. IteratorResult

IteratorResult는 다음 메서드가 호출될 때마다 얻은 데이터입니다.

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

얻을 데이터를 나타내는 값 외에도 IteratorResult에는 순회 완료 여부를 나타내는 완료도 있습니다.

다음은 배열을 순회하는 예입니다.

> 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 측에서 다운로드한 파일과 같은 비동기 데이터를 얻는 경우 파일을 한 줄씩 순회하려고 합니다. 데이터 행을 읽는 것은 비동기 작업이므로 여기에는 비동기 데이터 순회가 포함됩니다.

파일의 비동기 읽기를 추가하는 방법은 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은 동기 반복 가능 항목을 비동기 반복 가능 항목으로 변환합니다. 이에 대해서는 아래에서 설명하겠습니다. 이 섹션에서는 어떻게 생성되는지 살펴보겠습니다.

여기에서는 주로 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 }
}

비동기 반복 가능 탐색


동기 반복 가능을 탐색하려면 for-of를 사용하고, for-await -of can을 사용하세요. 비동기 반복 가능 트래버스.

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은 동기 반복자를 반환하고 다음 메서드는 {value, done }을 반환합니다.

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를 추가합니다.

일반 생성기의 경우 다음 메서드가 호출될 때마다 {value,done} 개체가 반환됩니다. 이 개체 개체는 항복 값을 캡슐화합니다.

비동기 생성기의 경우 다음 메서드가 호출될 때마다 {value,done} 개체가 포함된 Promise 개체가 반환됩니다. 이 개체 개체는 항복 값을 캡슐화한 것입니다.

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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 flydean的博客에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제