Typescript의 생성기

본 논문에서 우리는 자바스크립트/Typescript 중의 ES2015 생성기를 이해할 것이다.생성기는 교체기에 심각하게 의존하기 때문에 메모리를 모르거나 새로 고치려면 이전 글을 보십시오.

소개하다.
우리가 알고 있는 바와 같이, 교체기는 특정한 구조의 교체를 완전히 제어할 수 있다. 우리는 교체 서열의 다음 요소를 언제 얻을지, 그리고 교체기의 소비자들에게 어떻게 이러한 요소의 실현 세부 사항을 숨길지 결정할 수 있다.그러나 모든 일에는 대가가 있기 때문에, 교체기의 실현은 매우 까다로울 수 있다. 왜냐하면 우리는 실행 흐름을 제어하는 상태를 추적해서 (예를 들어) 교체기를 완전하게 표시할 수 있도록 해야 하기 때문이다.
생성기는 우리로 하여금 쉽게 교체기를 만들 수 있게 하여, 예를 들어 함수의 실행을 멈추고 나중에 그것들을 복원할 수 있게 한다. (async/await?)이러한 일시정지와 더 많은 일시정지 사이에 값을 생성기에 전달합니다.

토대
발전기는 매우 복잡할 수도 있고, 우리의 습관과 약간 다르기 때문에 세부 사항에 주의를 기울여 주십시오.생성기 선언은 함수 선언과 매우 비슷합니다.
function* fooGen() {
    console.log("Hello from fooGen");
}
function foo() {
    console.log("Hello from foo")
}
function* fooGen 정의 생성기를 사용할 수 있다(실제로 function * fooGen 또는 function *fooGen을 실행할 수 있다).이것은 생성기 성명과 foo 함수 성명 사이의 유일한 차이이지만, 그것들의 행위는 실제로는 매우 다르다.다음 사항을 고려하십시오.
foo(); // Hello from foo
fooGen(); //
우리는 foo에 대한 호출은 예상과 같지만 fooGen에 대한 호출은 아무런 내용도 기록하지 않았다.이것은 매우 이상한 것 같지만, 이것은 함수와 생성기 사이의 첫 번째 큰 차이이다.함수는 절박하다. 이것은 언제 호출하든지 바로 실행을 시작하고, 생성기는 게으르다는 것을 의미한다. 이것은 실행을 명확하게 알려줄 때만 우리의 코드를 실행한다는 것을 의미한다."하지만 실행하라고 명령할 수도 있지만, 호출 생성기는 코드를 실행하지 않고, 내부 초기화만 실행합니다.
그렇다면, 나는 어떻게 생성기에 우리의 코드를 실행하라고 알려야 합니까?우선 fooGen()이 우리에게 무엇을 가져다 주었는지 봅시다.fooGen의 유형을 살펴보면 function fooGen(): Generator<never, void, unknown>의 유형이 무엇인지 살펴보겠습니다.
interface Generator<T = unknown, TReturn = any, TNext = unknown> extends Iterator<T, TReturn, TNext> {
    // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
    next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
    return(value: TReturn): IteratorResult<T, TReturn>;
    throw(e: any): IteratorResult<T, TReturn>;
    [Symbol.iterator](): Generator<T, TReturn, TNext>;
}
등등, 이 인터페이스에는 Generator, nextreturn 방법이 있는데, 이것은 교체기 아닙니까?답은 긍정적이지만, 그것이iterable라는 것도 주의해야 한다.그래서 이 인터페이스는 사실상 throw 인터페이스와 약간 유사하다.왜 다른지 알고 싶으면 question을 보세요.
생성기에서 코드를 실행하려면 IterableIterator을 호출해야 합니다.
foo(); // Hello from foo
const it = fooGen();
it.next() // Hello from fooGen
생성기에서 몇 가지 값을 되돌려줍니다.
function* fooGen() {
    console.log("Hello from fGen");
    return "Bye from fGen";
}

const it = fooGen();
const result = it.next(); // Hello from fGen
console.log(result); // { value: 'Bye from fGen', done: true }
console.log(it.next()); // { value: undefined, done: true }
생성기에서 일부 내용을 되돌릴 때, 상태를 관리할 필요가 없이 교체기가 자동으로 완성됩니다.또한 next 표현식의 값은 한 번만 되돌아오고 return의 후속 호출은 값에서 it.next으로 되돌아오는 것을 주의하십시오.함수에 현식의 undefined 문장이 없거나 되돌아오는 논리적 지점에 도달하지 못하면 return이 되돌아오는 값이라고 가정하는 것을 기억하십시오.

키워드 undefined지금까지 우리는 발전기에 대해 어떤 흥분된 일도 하지 않았다. 우리는 단지 그것들을 더욱 복잡한 함수로 사용했을 뿐이다.인용문에서 말한 바와 같이, 우리는 생성기의 실행을 멈출 수 있다.우리는 yield 키워드를 사용하여 이 점을 실현했다.yield 키워드는 교체기의 실행을 정지합니다.
언제든지 yield을 호출하면 생성기는 next이나 yield 문장에 도달할 때까지 코드를 동기화합니다. (오류가 발생하지 않았다고 가정하면 잠시 후에 볼 수 있습니다.)생성기가 정지 상태에 있으면, return을 다시 호출하면, 정지된 위치에서 실행을 회복합니다.
function*  fooGen() {
    console.log("Begin execution");
    yield;
    console.log("End execution");
}

const it = fooGen();
it.next();
console.log("The generator is paused");
it.next();

// Begin execution
// The generator is paused
// End execution
우리는 next을 사용하여 생성기가 여러 개의 값을 되돌려받을 수 있도록 할 수 있다. (우리는 생성기가 이 값을 생성한다고 말한다.)Dell의 접근 방식은 다음과 같습니다.
function*  fooGen() {
    console.log("Begin execution");
    yield "This value was yielded";
    console.log("End execution");
}

const it = fooGen();
console.log(it.next());
console.log("The generator is paused");
it.next();
// Begin execution
// { value: 'This value was yielded', done: false }
// The generator is paused
// End execution
yield을 사용하면 생성기 교체기를 완성할 수 없습니다.이것은 매우 강력하다.이런 행위가 유용한 예는 메모리 효율이 높은 방식으로 (무한) 서열을 생성하는 것이다. 예를 들어 생성기를 어떻게 사용하여 Fibonacci sequence을 실현하는지 보자.
function* fibonacciGenerator() {
    const f0 = 0;
    yield f0;
    const f1 = 1;
    yield f1;
    let previousValue = f0, currentValue = f1, nextValue;
    while(true) {
        nextValue = previousValue + currentValue;
        previousValue = currentValue;
        currentValue = nextValue;
        yield nextValue;
    }
}

const it = fibonacciGenerator();
console.log(it.next().value); // 0
console.log(it.next().value); // 1
console.log(it.next().value); // 1
console.log(it.next().value); // 2
console.log(it.next().value); // 3
생성기의 게으름은 매우 유용합니다. 실행을 멈추는 능력은 언제든지 서열의 무한 요소를 생성할 수 있습니다. (가능한 정수 넘침을 무시할 수 있습니다.) 이전의 값과 현재 값만 저장할 수 있습니다.괜찮죠?비록 나는 이렇게 하는 것을 권장하지 않지만, 우리는 실제적으로 생성기를 완성할 필요가 없다. 우리는 단지 약간의 값을 취하고, yield을 더 이상 호출하지 않을 수도 있다.

생성기에 값 전달
생성기에 값을 전달할 수 있는 두 가지 방법이 있다.그 중 하나는 우리가 생성기 교체기를 만들 때 함수에 한 것처럼피보나치 예를 확장하여 시퀀스의 시작 위치를 선택합니다.
function* fibonacciGenerator(startingPosition = 1) {
    const f0 = 0;
    if(startingPosition === 1) {
        yield f0;
    }
    const f1 = 1;
    if(startingPosition <= 2) {
        yield f1;
    }
    let previousValue = f0, currentValue = f1, nextValue;
    let currentPosition = 3;
    while(true) {
        nextValue = previousValue + currentValue;
        previousValue = currentValue;
        currentValue = nextValue;
        if(currentPosition >= startingPosition){
            yield nextValue;
        } else {
            currentPosition += 1;
        }
    }
}

const it = fibonacciGenerator();
console.log(it.next().value); // 0
console.log(it.next().value); // 1
console.log(it.next().value); // 1
console.log(it.next().value); // 2
console.log(it.next().value); // 3

console.log();

const it2 = fibonacciGenerator(4);
console.log(it2.next().value); // 2
console.log(it2.next().value); // 3
console.log(it2.next().value); // 5
console.log(it2.next().value); // 8
console.log(it2.next().value); // 13
생성기에 값을 전달하는 또 다른 방법은 next을 통해지금까지 우리는 yield을 사용하여 생성기에서 값을 만들어 왔기 때문에 곤혹스러울 수 있습니다.사실상 yield은 표현식으로 계산 결과가 어떤 값이라는 것을 의미한다.규명하기 위해서 이 예를 살펴보자.
function* fooGen() {
    while(true) {
        console.log(yield);
    }
}

const it = fooGen();
it.next();
it.next(1); // 1
it.next(2); // 2
it.next("heey"); // heey
yield의 첫 번째 호출은 생성기 교체기의 실행을 직접 시작합니다.it.next() 표현식을 찾으면 실행을 중지합니다.우리가 yield을 할 때마다 it.next(1)yield의 값으로 평가되기 때문에 우리는 1을 가지고 있다.
허용되는 사항은 다음과 같습니다.
function* accumulator(startingValue = 0): Generator<number, any, number> {
    let value = startingValue;
    while(true) {
        const input = yield value;
        value += input;
    }
}

const it = accumulator();
it.next();
console.log(it.next(3).value); // 3
console.log(it.next(10).value); // 13
console.log(it.next(-3).value); // 10
먼저 코드를 실행하고 console.log(1)을 찾을 때까지 yield(value)을 생성합니다.언제든지 startingValue을 호출하면 표현식 next(3)의 계산 결과는 yield value이기 때문에 현재는 3, 그 다음은 input === 3이다.그런 다음 반복됩니다.
위에는 유형에 대한 평론이 있다.Typescript가 value === 3의 유형을 자동으로 감지할 수 있도록 위의 생성기를 현저하게 입력해야 합니다.굴복 표현식의 유형은 ongoing struggle으로 추정된다.
주의: input에 전달된 첫 번째 호출이 무엇이든 무시될 수 있으니 조심하세요.

오류 처리
Google 생성기의 코드는 다른 함수 코드와 마찬가지로 next개의 블록을 넣을 수 있습니다.
function* fooGen() {
    try {
        throw "Hi";
    } catch(err) {
        console.log("Err caught in fooGen:", err);
    }
    return "End of execution";
}

const it = fooGen();
it.next();
console.log(it.next())

// Err caught in fooGen: Hi
// { value: "End of execution", done: true }
// { value: undefined, done: true }
이상을 처리한 후 생성기가 계속 실행된다는 것을 주의하십시오.만약 발전기 안에 try...catch이 없다면 이상은 정상적인 상황처럼 거품이 생길 것이다.
function* fooGen() {
    throw "Hi";
    return "End of execution";
}

const it = fooGen();
try {
    it.next();
} catch(err) {
    console.log("Exception caught outside of generator: ", err);
}
console.log(it.next());

// Exception caught outside of generator:  Hi
// { value: undefined, done: true }
포획되지 않은 이상으로 인해, Google 생성기가 완성되었고, 되돌아오는 문장에 도달하지 않았습니다.
생성기 외부에서 내부로 오류를 전달할 수도 있습니다.
function* fooGen() {
    console.log("Beginning of execution");
    try {
        yield;
    } catch(err) {
        console.log("Error caught inside fooGen: ", err);
    }
    return "End of execution";
}

const it = fooGen();
it.next();
console.log(it.throw("Hi from outside"));
console.log(it.next());

// Beginning of execution
// Error caught inside fooGen:  Hi from outside
// { value: 'End of execution', done: true }
// { value: undefined, done: true }
오류는 생성기가 일시 정지할 때 던져진 것입니다.만약 당시에 try...catch이 없었다면, 그것은 평소와 같이 거품을 일으켰을 것이다.
우리가 try...catch을 사용하고 싶은 예는 우리의 피보나치 예이다.구현됨에 따라 최종적으로 overflow을 만나게 됩니다.우리는 bigInt을 사용하여 이런 상황을 피할 수 있다.우리의 예에서, 우리는 넘침이 발생할 때만 교체기를 완성하고 싶을 뿐이다.
function* fibonacciGenerator() {
    const f0 = 0;
    yield f0;
    const f1 = 1;
    yield f1;
    let previousValue = f0, currentValue = f1, nextValue;
    try {
        while(true) {
            nextValue = previousValue + currentValue;
            previousValue = currentValue;
            currentValue = nextValue;
            yield nextValue;
        }
    } catch(err) {
        return;
    }
}
let flag = true;
let value: number | void;
const it = fibonacciGenerator();
while(flag) {
    value = it.next().value;
    if(value === Number.MAX_SAFE_INTEGER || !Number.isFinite(value)) {
        it.throw("overflow");
        console.log("overflow detected");
        console.log(it.next());
        flag = false;
    } else {
        console.log(value);
    }
}
생성기 외부에서 넘침이 검출될 때마다 Generator.throw을 호출하면 다른 쓰레기 값이 생성되지 않습니다.

발전기 대표단
generator delegation it.throw 구문을 사용하여 둘 이상의 generator를 조합할 수 있습니다.
function* g1() {
    yield 2;
    yield 3;
    yield 4;
  }

function* g2() {
    yield 1;
    yield* g1();
    yield 5;
  }

const iterator = g2();
console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: 4, done: false}
console.log(iterator.next()); // {value: 5, done: false}
console.log(iterator.next()); // {value: undefined, done: true}
발생하는 상황은 yield*을 만날 때마다 그 다음의 yield* 또는 next은 위탁 생성기로 넘어가는데 이 예에서 throw이다.g2이 완성되고 g2의 완성치는 g2이다.yield* g2()이 완성되면 next에서 g1에 대한 후속 호출은 정상적으로 정지된 g2에서 계속된다.이것이 바로 자바스크립트로 coroutines을 작성하는 방법이다.
실제로, 당신은 g1을 모든iterable와 함께 사용할 수 있습니다. 예를 들어, 그룹 등입니다.

결론
생성기는 자바스크립트에서 약간 모호하지만 매우 재미있는 구조이다.너는 야외에서 발전기를 찾을 수 없을지도 모르지만, 그것들의 존재를 아는 것은 좋은 일이다.
너는 생성기로 매우 멋진 것을 구축할 수 있다. Async/Await는 생성기와 약속으로 이루어진 것이다.만약 당신이 더 많은 것을 알고 싶다면, 나의 다음 문장을 참고하세요.
질문이나 권장 사항은 언제든지 의견을 추가하십시오.다음까지 안전하게 유지:)

좋은 웹페이지 즐겨찾기