Node.js의 비동기 생성기와 비동기 교체 상세 설명

앞말


생성기 함수는 JavaScript에서 async/await를 도입하는 것보다 먼저 나타났습니다. 이것은 비동기 생성기 (시종일관 Promise를 되돌려주고 await를 할 수 있는 생성기) 를 만드는 동시에 주의해야 할 사항을 많이 도입했다는 것을 의미합니다.
오늘 우리는 비동기 생성기와 그 근친인 비동기 교체를 연구할 것이다.
주의: 비록 이러한 개념은 현대 규범을 따르는javascript에 적용되어야 하지만 본고의 모든 코드는 Node를 대상으로 한다.js 10, 12, 14판 개발 및 테스트.

비동기 생성기 함수


이 프로그램을 보십시오.

// File: main.js
const createGenerator = function*(){
 yield 'a'
 yield 'b'
 yield 'c'
}

const main = () => {
 const generator = createGenerator()
 for (const item of generator) {
 console.log(item)
 }
}
main()

이 코드는 생성기 함수를 정의합니다. 이 함수로 생성기 대상을 만들고 for...of 이 생성기 대상을 순환합니다.비록 너는 절대로 실제 업무에서 생성기로 이런 자질구레한 일을 처리하지 않을 것이다.생성기와 for에 익숙하지 않으면..of 순환, 보십시오.및이 두 편의 문장.비동기 생성기를 사용하기 전에 생성기와 for...of 순환은 튼튼한 이해가 있다.
만약 우리가 생성기 함수에await를 사용하려고 한다면, async 키워드로 함수를 설명해야 한다면, Node.js는 이 기능을 지원합니다.만약 당신이 비동기 함수에 익숙하지 않다면, 을 보십시오.한 푼.
다음은 프로그램을 수정하고 생성기에서await를 사용합니다.

// File: main.js
const createGenerator = async function*(){
 yield await new Promise((r) => r('a'))
 yield 'b'
 yield 'c'
}

const main = () => {
 const generator = createGenerator()
 for (const item of generator) {
 console.log(item)
 }
}
main()

마찬가지로 실제 업무에서도 당신은 이렇게 하지 않을 것이다. 당신은 제3자 API나 라이브러리에서 온 함수일 수도 있다.모두가 쉽게 파악할 수 있도록 우리의 예는 가능한 한 간단하게 유지한다.
위의 프로그램을 실행하려고 하면 문제가 발생합니다.

$ node main.js
/Users/alanstorm/Desktop/main.js:9
 for (const item of generator) {
 ^
TypeError: generator is not iterable
JavaScript는 이 생성기가 "교체할 수 없는"것이라고 알려 줍니다.언뜻 보기에는 생성기 함수를 비동기화시키는 것 같아서 생성기가 교체할 수 없다는 것을 의미한다.이것은 약간 곤혹스럽다. 왜냐하면 생성기의 목적은'프로그래밍 방식'으로 교체할 수 있는 대상을 생성하는 것이기 때문이다.
다음은 도대체 무슨 일이 일어났는지 알아내자.

생성기 확인


자바스크립트 생성기[1]의 교체 대상을 보았다면.대상이 넥스트 방법을 가지고 있을 때, 이 대상은 교체기 프로토콜을 실현하고, 이 넥스트 방법은value 속성, done 속성 중 하나 또는value와done 속성을 가진 대상을 되돌려줍니다.
다음 코드를 사용하여 비동기 생성기 함수와 일반 생성기 함수가 반환하는 생성기 객체를 비교합니다.

// File: test-program.js
const createGenerator = function*(){
 yield 'a'
 yield 'b'
 yield 'c'
}

const createAsyncGenerator = async function*(){
 yield await new Promise((r) => r('a'))
 yield 'b'
 yield 'c'
}

const main = () => {
 const generator = createGenerator()
 const asyncGenerator = createAsyncGenerator()

 console.log('generator:',generator[Symbol.iterator])
 console.log('asyncGenerator',asyncGenerator[Symbol.iterator])
}
main()

전자에는 Symbol이 없습니다.iterator 방법은 후자에 있습니다.

$ node test-program.js
generator: [Function: [Symbol.iterator]]
asyncGenerator undefined
이 두 생성기 대상은 모두next 방법이 있다.테스트 코드를 수정해서 이next 방법을 호출한다면:

// File: test-program.js

/* ... */

const main = () => {
 const generator = createGenerator()
 const asyncGenerator = createAsyncGenerator()

 console.log('generator:',generator.next())
 console.log('asyncGenerator',asyncGenerator.next())
}
main()

다른 질문이 표시됩니다.

$ node test-program.js
generator: { value: 'a', done: false }
asyncGenerator Promise { <pending> }
대상을 교체할 수 있도록,next 방법은value와done 속성이 있는 대상을 되돌려야 합니다.async 함수는 항상 Promise 객체를 반환합니다.이 기능은 비동기 함수로 만든 생성기에 가져옵니다. 이 비동기 생성기들은 시종일관 Promise 대상입니다.
이런 행위로 인해 async 함수의 생성기가javascript 교체 프로토콜을 실현할 수 없습니다.

비동기적 교체


다행히도 이 모순을 해결할 방법이 있다.async 생성기가 되돌아오는 구조 함수나 클래스를 보면

// File: test-program.js
/* ... */
const main = () => {
 const generator = createGenerator()
 const asyncGenerator = createAsyncGenerator()

 console.log('asyncGenerator',asyncGenerator)
}

객체의 유형이나 클래스 또는 구조 함수가Generator가 아닌 AsyncGenerator인 것을 볼 수 있습니다.

asyncGenerator Object [AsyncGenerator] {}
비록 이 대상은 교체할 수 있는 것이 아닐 수도 있지만, 그것은 비동기적으로 교체할 수 있는 것이다.
객체를 비동기적으로 교체하려면 Symbol을 구현해야 합니다.asyncIterator 방법.이 방법은 반드시 하나의 대상을 되돌려야 한다. 이 대상은 비동기적인 버전의 교체기 프로토콜을 실현했다.즉, 대상은 Promise를 되돌려주는next 방법이 있어야 하고, 이promise는 최종적으로done와value 속성을 가진 대상으로 해석되어야 한다.
AsyncGenerator 객체는 이러한 모든 조건을 충족합니다.
이것은 하나의 문제를 남겼다. 우리는 어떻게 해야만 교체할 수 없지만 다른 단계로 교체할 수 있는 대상을 두루 겪을 수 있습니까?

for await... of 순환.


생성기의next 방법만으로 수동으로 이종 교체 대상을 교체할 수 있다.(여기main 함수는 현재 asyncmain입니다. 이렇게 하면 함수 내부에서await를 사용할 수 있습니다.)

// File: main.js
const createAsyncGenerator = async function*(){
 yield await new Promise((r) => r('a'))
 yield 'b'
 yield 'c'
}

const main = async () => {
 const asyncGenerator = createAsyncGenerator()

 let result = {done:false}
 while(!result.done) {
 result = await asyncGenerator.next()
 if(result.done) { continue; }
 console.log(result.value)
 }
}
main()

그러나 이것은 가장 직접적인 순환 메커니즘이 아니다.나는 while의 순환 조건을 좋아하지 않을 뿐만 아니라, 수동으로result를 검사하고 싶지도 않다.done.또한,result.done 변수는 내부와 외부 블록의 작용역에 동시에 존재해야 합니다.
다행히도 대다수 (아마도 모두?)비동기 교체기를 지원하는javascript 구현도 모두 특수한 for await를 지원합니다...of 순환 문법.예:

const createAsyncGenerator = async function*(){
 yield await new Promise((r) => r('a'))
 yield 'b'
 yield 'c'
}

const main = async () => {
 const asyncGenerator = createAsyncGenerator()
 for await(const item of asyncGenerator) {
 console.log(item)
 }
}
main()

상기 코드를 실행하면 비동기 생성기와 교체 가능한 대상이 성공적으로 순환되었고 순환체에서 Promise의 완전한 해석 값을 얻을 수 있습니다.
$ node main.js
a
b
c
이거 for await...of 순환은 비동기 교체기 프로토콜을 실현하는 대상을 더욱 좋아한다.하지만 너는 그것으로 어떤 교체 가능한 대상을 두루 훑어볼 수 있다.

for await(const item of [1,2,3]) {
 console.log(item)
}
for await를 사용할 때 Node.js는 먼저 대상에서 Symbol을 찾을 것입니다.asyncIterator 방법.찾을 수 없는 경우 Symbol 사용으로 돌아갑니다.iterator 방법.

비선형 코드 실행


await와 마찬가지로, for await 순환은 비선형 코드 실행을 프로그램에 도입합니다.즉, 당신의 코드는 작성된 코드와 다른 순서로 실행될 것입니다.
프로그램이 forawait 순환을 처음 만났을 때, 대상에next를 호출합니다.
이 대상은 yield의promise를 사용하고 코드의 실행은 async 함수를 떠나며 프로그램은 이 함수 밖에서 계속 실행됩니다.
프로미스가 해결되면, 코드 실행은 이 값을 사용하여 순환체로 되돌아옵니다.
순환이 끝나고 다음 일정을 진행할 때, Node.js는 대상에next를 호출합니다.이 호출은 또 다른promise를 생성합니다. 코드 실행은 함수를 다시 벗어날 것입니다.Promise가done이true의 대상으로 해석될 때까지 이 모드를 반복하고 forawait 순환 후에 코드를 계속 실행합니다.
다음 예제에서는 다음과 같이 설명할 수 있습니다.

let count = 0
const getCount = () => {
 count++
 return `${count}. `
}

const createAsyncGenerator = async function*() {
 console.log(getCount() + 'entering createAsyncGenerator')

 console.log(getCount() + 'about to yield a')
 yield await new Promise((r)=>r('a'))

 console.log(getCount() + 're-entering createAsyncGenerator')
 console.log(getCount() + 'about to yield b')
 yield 'b'

 console.log(getCount() + 're-entering createAsyncGenerator')
 console.log(getCount() + 'about to yield c')
 yield 'c'

 console.log(getCount() + 're-entering createAsyncGenerator')
 console.log(getCount() + 'exiting createAsyncGenerator')
}

const main = async () => {
 console.log(getCount() + 'entering main')

 const asyncGenerator = createAsyncGenerator()
 console.log(getCount() + 'starting for await loop')
 for await(const item of asyncGenerator) {
 console.log(getCount() + 'entering for await loop')
 console.log(getCount() + item)
 console.log(getCount() + 'exiting for await loop')
 }
 console.log(getCount() + 'done with for await loop')
 console.log(getCount() + 'leaving main')
}

console.log(getCount() + 'before calling main')
main()
console.log(getCount() + 'after calling main')

이 코드는 일련 번호의 로그 기록 문장을 사용해서 실행 상황을 추적할 수 있습니다.연습으로 프로그램을 실행하고 실행 결과가 어떤지 확인해야 한다.
만약 네가 그것의 작업 방식을 모른다면, 프로그램의 집행에 혼란을 일으킬 것이다. 그러나 비동기적인 교체는 확실히 강력한 기술이다.

총결산


여기 Node에 관한 이야기입니다.js에서 비동기 생성기와 비동기 교체에 관한 글은 여기에 소개되었습니다.js 비동기 생성기와 비동기 교체 내용은 이전의 글을 검색하거나 아래의 관련 글을 계속 훑어보십시오. 앞으로 많은 응원 부탁드립니다!

좋은 웹페이지 즐겨찾기