ECMAScript 6 (21)Generator 함수의 구문

11283 단어 ES6 시리즈es6

1.무엇

  • Generator 함수를 호출한 후 이 함수는 실행되지 않습니다
  • 호출 후 결과를 반환하지 않고 객체 및 반복자 객체(Iterator Object)를 반환합니다.
  • 대상은next() 방법을 호출하여 다음 단계로 들어갑니다.간단하게 말하면Generator 함수는 세그먼트로 실행되며, yield 표현식은 실행을 정지하는 표시이며,next 방법은 실행을 회복할 수 있습니다.
  • return 문장이 없으면 함수 끝까지 실행
  • done 속성은 브리 값으로 스트리밍이 끝났는지 여부를 나타낸다.done 속성의 값true, 반복이 끝났음을 표시합니다.
  • yield 표현식 뒤에 있는 표현식은next 메서드, 내부 포인터가 해당 문장을 가리킬 때만 실행되므로 JavaScript에 수동적인'불활성 값 구하기'(Lazy Evaluation) 문법 기능을 제공하는 것과 같습니다.
  • function* test(){
        yield 'hello';
        yield 'world';
        return 'end'; 
    }
    var tt = test()
    tt.next() // {value: "hello", done: false}
    tt.next() // {value: "world", done: false}
    tt.next() // {value: "end", done: false}
    tt.next() // {value: undefined, done: true}
    
  • Generator 함수는 yield 표현식을 사용하지 않을 수 있는데 이것은 단순한 임시 집행 함수가 되었다.
  • function* f() {
      console.log(' !')
    }
    
    var generator = f();
    
    setTimeout(function () {
      generator.next()
    }, 2000);
    
  • yield 표현식이 다른 표현식에 사용되면 괄호 안에 넣어야 합니다.
  • function* demo() {
      console.log('Hello' + yield); // SyntaxError
      console.log('Hello' + yield 123); // SyntaxError
    
      console.log('Hello' + (yield)); // OK
      console.log('Hello' + (yield 123)); // OK
    }
    
  • yield 표현식은 함수 매개 변수로 사용하거나 부치 표현식의 오른쪽에 놓고 괄호를 넣지 않아도 된다.
  •  function* demo() {
      foo(yield 'a', yield 'b'); // OK
      let input = yield; // OK
    }
    
  • 객체의 Symbol.iterator 방법은 이 대상의 플러그인 생성 함수와 같습니다. 이 함수를 호출하면 이 대상의 플러그인 대상을 되돌려줍니다.
  • var myIterable = {};
    myIterable[Symbol.iterator] = function* () {
      yield 1;
      yield 2;
      yield 3;
    };
    
    [...myIterable] // [1, 2, 3]
    
  • Generator 함수를 실행한 후, 범람기 대상을 되돌려줍니다.객체 자체에도 Symbol이 있습니다.iterator 속성, 실행 후 자신을 되돌려줍니다.
  • function* gen(){
        yield 1;
        yield 2;
    }
    
    var g = gen();
    
    [...g]  // [1,2]
    

    2. next()

  • next 방법의 매개 변수는 이전 yield 표현식의 반환값을 나타낸다
  • 따라서next 방법을 처음 사용할 때 전달 매개 변수는 무효입니다.V8 엔진은 첫 번째next 방법을 사용할 때의 매개 변수를 직접 무시합니다. 두 번째next 방법을 사용할 때부터만 매개 변수가 유효합니다.의미에서 첫 번째next 방법은 플러그인 대상을 시작하는 데 사용되기 때문에 파라미터를 가지고 있지 않습니다.
  • function* foo(x) {
      var y = 2 * (yield (x + 1));
      var z = yield (y / 3);
      return (x + y + z);
    }
    
    var a = foo(5);
    a.next() // Object{value:6, done:false}
    a.next() // Object{value:NaN, done:false}
    a.next() // Object{value:NaN, done:true}
    
    var b = foo(5);
    b.next() // { value:6, done:false }
    b.next(12) // { value:8, done:false }
    b.next(13) // { value:42, done:true }
    

    3. for…of

  • for...of 순환은Generator 함수가 실행될 때 생성된 Iterator 대상을 자동으로 옮겨다니며,next 방법을 호출할 필요가 없습니다.
  • next 방법의 귀환 대상의done 속성이true이면 for...of 순환이 중단되고 이 귀환 대상이 포함되지 않기 때문에 위 코드의return 문장이 귀환하는 6은 for...of 순환에 포함되지 않습니다.
  • function* foo() {
      yield 1;
      yield 2;
      yield 3;
      yield 4;
      yield 5;
      return 6;
    }
    
    for (let v of foo()) {
      console.log(v);
    }
    // 1 2 3 4 5
    
    function* numbers () {
      yield 1
      yield 2
      return 3
      yield 4
    }
    
    //  
    [...numbers()] // [1, 2]
    
    // Array.from  
    Array.from(numbers()) // [1, 2]
    
    //  
    let [x, y] = numbers();
    x // 1
    y // 2
    
    // for...of  
    for (let n of numbers()) {
      console.log(n)
    }
    // 1
    // 2
    

    4. Generator.prototype.throw()

  • Generator 함수가 되돌아오는 범람기 대상은throw 방법으로 함수 밖에서 오류를 던진 다음Generator 함수에서 포획할 수 있다.
  • throw 방법은 매개 변수를 받아들일 수 있습니다. 이 매개 변수는catch 문장에 의해 수신됩니다. Error 대상의 실례를 던지는 것을 권장합니다.
  • throw 방법이 던진 오류는 내부에서 포착되어야 하며, 전제는next 방법을 최소한 한 번 실행해야 한다는 것이다.
  • var g = function* () {
      try {
        yield;
      } catch (e) {
        console.log(' ', e);
      }
    };
    
    var i = g();
    i.next();
    
    try {
      i.throw('a');
      i.throw('b');
    } catch (e) {
      console.log(' ', e);
    }
    //   a
    //   b
    
  • throw 방법이 포획되면 다음 yield 표현식을 추가합니다.즉, 넥스트 방법을 한 번 더 실행할 것이다.
  • var gen = function* gen(){
      try {
        yield console.log('a');
      } catch (e) {
        // ...
      }
      yield console.log('b');
      yield console.log('c');
    }
    
    var g = gen();
    g.next() // a
    g.throw() // b
    g.next() // c
    
  • Generator가 실행하는 과정에서 오류가 발생하고 내부에 포획되지 않으면 더 이상 실행하지 않습니다.간단하게 말하면 안에 잘못 보고된 것이 안에catch에 도착하지 않으면 함수는 바로 끝난다.
  • 범람기의throw 방법은Generator 함수 내부로 오류를 전송할 수 있다.
  • Generator 함수 내부는try...catch를 통해 오류를 포획할 수 있으며, 포획되지 않으면Generator 함수 밖으로 거품을 낸다.
  • Generator 함수 내부에서 오류가 정상적으로 포착되면 코드는 계속 실행됩니다.
  • 오류가 발생하지 않으면 플러그인의done는true로 변합니다.

  • 5.Generator.prototype.return()

  • Generator 함수가 되돌아오는 트랙터 대상과 주어진 값을 되돌려주고 Generator 함수를 끝낼 수 있는return 방법이 있습니다.
  • return 방법이 호출될 때 파라미터를 제공하지 않으면 되돌아오는 값의value 속성은undefined
  • 이다.
    function* gen() {
      yield 1;
      yield 2;
      yield 3;
    }
    
    var g = gen();
    
    g.next()        // { value: 1, done: false }
    g.return('foo') // { value: "foo", done: true }
    g.next()        // { value: undefined, done: true }
    
  • Generator 함수 내부에try...finally 코드 블록이 있고try 코드 블록을 실행하고 있으면return 방법은finally 코드 블록에 바로 들어가고 실행이 끝난 후에야 전체 함수가 끝납니다.
  • function* numbers () {
      yield 1;
      try {
        yield 2;
        yield 3;
      } finally {
        yield 4;
        yield 5;
      }
      yield 6;
    }
    var g = numbers();
    g.next() // { value: 1, done: false }
    g.next() // { value: 2, done: false }
    g.return(7) // { value: 4, done: false }
    g.next() // { value: 5, done: false }
    g.next() // { value: 7, done: true }
    

    6. yield*


    Generator 함수 내부에서 다른 Generator 함수를 호출합니다.전자의 함수체 내부에서 수동으로 옮겨다니는 것이 필요하다.
    function* foo() {
      yield 'a';
      yield 'b';
    }
    
    function* bar() {
      yield 'x';
      //   foo()
      for (let i of foo()) {
        console.log(i);
      }
      yield 'y';
    }
    
    for (let v of bar()){
      console.log(v);
    }
    // x
    // a
    // b
    // y
    
  • ES6는 이eld* 표현식을 제공하여 해결 방법으로 한 Generator 함수에서 다른 Generator 함수를 집행하는 데 사용한다.
  • yield* 뒤에 있는 Generator 함수(return 문장이 없을 때)는 Generator 함수 내부에 for...of 순환을 배치하는 것과 같다.
  • function* bar() {
      yield 'x';
      yield* foo();
      yield 'y';
    }
    
    //  
    function* bar() {
      yield 'x';
      yield 'a';
      yield 'b';
      yield 'y';
    }
    
    //  
    function* bar() {
      yield 'x';
      for (let v of foo()) {
        yield v;
      }
      yield 'y';
    }
    
    for (let v of bar()){
      console.log(v);
    }
    // "x"
    // "a"
    // "b"
    // "y"
    
  • return 문장이 있을 때varvalue = yield* iterator 형식으로return 문장의 값을 가져와야 합니다.
  • function* foo() {
      yield 2;
      yield 3;
      return "foo";
    }
    
    function* bar() {
      yield 1;
      var v = yield* foo();
      console.log("v: " + v);
      yield 4;
    }
    
    var it = bar();
    
    it.next()
    // {value: 1, done: false}
    it.next()
    // {value: 2, done: false}
    it.next()
    // {value: 3, done: false}
    it.next();
    // "v: foo"
    // {value: 4, done: false}
    it.next()
    // {value: undefined, done: true}
    

    7. 객체 속성인 Generator 함수

    let obj = {
      * myGeneratorMethod() {
        ···
      }
      //  
       myGeneratorMethod: function* () {
        // ···
      }
    };
    

    8. this

  • Generator 함수의this는 window
  • 를 가리킨다
    function *g() {
        console.log(this)
        yield 'g'
    }
    
    let foo = g()
    foo.next()
    // Window
    // {value: "g", done: false}
    
  • Generator 함수는 new를 통해 범람기를 만들 수 없기 때문에this지향을 바꾸면call,apply,bind
  • function* F() {
      this.a = 1;
      yield this.b = 2;
      yield this.c = 3;
    }
    var obj = {};
    var f = F.call(obj);
    
    f.next();  // Object {value: 2, done: false}
    f.next();  // Object {value: 3, done: false}
    f.next();  // Object {value: undefined, done: true}
    
    obj.a // 1
    obj.b // 2
    obj.c // 3
    
  • 다른 하나는 F.prototype에 직접 묶는 것이다
  • function* F() {
      this.a = 1;
      yield this.b = 2;
      yield this.c = 3;
    }
    var f = F.call(F.prototype);
    
    f.next();  // Object {value: 2, done: false}
    f.next();  // Object {value: 3, done: false}
    f.next();  // Object {value: undefined, done: true}
    
    f.a // 1
    f.b // 2
    f.c // 3
    

    9. 상태기

  • 이곳의 상태기는 통상적으로 유한상태기를 가리킨다.
  • javascript의 일반적인 동작은 전역 변수 flag를 정의한 다음에 flag=!flag 상태를 변경합니다. jquery의 toggle 동작과 유사합니다.
  • var flag = true;
    var toggle = function(){
        if(flag)
            // show
        else
            // hide
    
        flag = !flag
    }
    
  • Generator 사용
  • var toggle = function* () {
      while (true) {
        // show
        yield;
        // hide
        yield;
      }
    };
    
  • 의 장점은 전체적인 상태 변수를 정하지 않아도 더욱 간결하고 안전하며 함수식 프로그래밍에 부합되는 사상이 있고 쓰기 방법도 우아하다.

  • 10. 적용

  • Generator 함수의 주요 목적은 비동기 프로그래밍을 해결하는 데 사용되며 비동기 프로그래밍의 작성 난이도를 크게 줄일 수 있다.
  • 예를 들어 만약에 우리가 5개의 비동기 요청이 있고 그 다음에 이전의 비동기 요청의 결과에 따라 요청을 완성해야 한다면 코드의 복잡도는 급격히 상승할 것이다.
  • function delay(msg, callback) {
        setTimeout(function () {
            console.log(msg)
            callback()
        }, 1000)
    }
    delay('1', function () {
        delay('2', function () {
            delay('3', function () {
                delay('4', function () {
                    delay('5', function () {
                        console.log('done')
                    })
                })
            })
        })
    })
    
  • es6에 추가된promise는 비동기 프로그래밍을 해결할 때 어느 정도 난이도를 낮추었지만 주로 한 개의 비동기 요청에 대한 난이도를 낮추어 간단하고 가볍게 보였다.오류 처리 능력도 강화됐지만 위와 같은 상황에 대한 해결은 질적으로 향상되지 않았다.
  • function delay(msg) {
        return new Promise((resolve, reject) => {
            setTimeout(function () {
                console.log(msg)
                resolve()
            }, 1000)
        })
    }
    delay('1')
        .then(() => delay('2'))
        .then(() => delay('3'))
        .then(() => delay('4'))
        .then(() => delay('5'))
        .then(() => {
            console.log('done')
        })
    

    - 위의 코드는 결합도가 높고 모든 비동기가 프로미스이어야 하며 그렇지 않으면 안 된다.그래서 Generator 함수가 등장하여 이런 장면을 해결하는 가장 좋은 방법이다.
    function delay(msg) {
        setTimeout(function () {
            iter.next(msg)
        }, 1000)
    }
    
    function*g() {
        let result1 = yield delay('1')
        console.log(result1)
        let result2 = yield delay('2')
        console.log(result2)
        let result3 = yield delay('3')
        console.log(result3)
        let result4 = yield delay('4')
        console.log(result4)
        let result5 = yield delay('5')
        console.log(result5)
        console.log('done')
    }
    let iter = g()
    iter.next()
    
  • 이 방법은 다음과 같은 특징을 가지고 있다.
  • 동기화 코드처럼 여러 개의 비동기 요청을 작성하는데 동기화 프로그래밍 쓰기는Promise나 리셋 함수 세트보다 간단하다.
  • 결합을 풀고 각 비동기 함수는 마지막에iter를 붙인다.next () 방법은 상태기를 다음 상태로 밀어내는 데 사용되지만, 다음 상태가 어떤 비동기적인 요청인지 신경 쓸 필요가 없습니다.
  • iter를 통해.next()의 매개 변수는 지난번에 요청한 데이터를 다음에 요청한 데이터로 전송할 수 있으며 간단하고 폭력적입니다.
  • 함수의 매개 변수 (예: dalay (result1)) 를 통해 지난번 비동기 요청의 결과를 현재 비동기 요청에 전달합니다.

  • 10.2 배포 범람기 인터페이스

    let obj = {
        a: 1,
        b: 2,
        c: 3,
        *[Symbol.iterator]() {
            yield this.c
            yield this.b
            yield this.a
        }
    }
    let arr = [...obj];
    arr;    // [3, 2, 1]
    

    참고 문헌: 완일봉es6

    좋은 웹페이지 즐겨찾기