비동기 프로그래밍 - es6를 이해하는Generators (생성기)

46195 단어 ES6/ES7/8/9...

앞말


Generators를 배우기 전에 에스6의 교체기: 교체기 상세한 설명을 이해해 봅시다.

본문


Generators란 무엇입니까?


생성기도 ES6에 새로 추가된 기능입니다.그것의 작법은 함수와 매우 비슷하다. 단지 성명할 때'*'번호가 하나 더 생겼을 뿐이다.
function* say(){}
const say = function*(){}

주의: 이 ※ function 키워드 뒤에만 쓸 수 있습니다.
그러나 생성기와 일반 함수는 문법상의 차이만 있는 것이 아니다. 본질적으로 일반 함수는 호출된 후에 반드시 이 함수를 실행하여 함수가 끝나거나return될 때까지 중도에 멈출 수 없고 생성기는 멈출 수 있는 함수이다.
function* say(){
    yield " ";
    yield " ";
    yield " ";
}
let it = say(); //  say , 
console.log(it) // [object Generator]
console.log(it.next()); // { value: ' ', done: false }
console.log(it.next()); // { value: ' ', done: false }
console.log(it.next()); // { value: ' ', done: false }
console.log(it.next()); // { value: undefined, done: true }

생성기를 호출하는say 방법은 이때의 함수가 실행되지 않아서 얻은 것은 하나의 교체기이다. 앞에서 말한 교체기의 인식을 통해 우리는 교체기next() 방법을 호출하여say 함수가 실행되기 시작하고,yield를 만났을 때 함수가value 속성을 포함하고 그 값은 yield 뒤에 따라오는 데이터이며 done의 값은false이다.넥스트를 다시 실행하면 함수가 활성화되고 다음 yield가 나타날 때까지 계속 실행됩니다.모든 yield가 실행되었을 때,next를 다시 호출할 때 얻은value는undefined,done의 값은true입니다.
물론Generator 함수는 yield 표현식을 사용하지 않아도 되는데, 이때 단순한 실행 유예 함수가 되었다.
function* f() {
  console.log(' !')
}
var generator = f();
setTimeout(function () {
  generator.next()
}, 2000);   //  -> " !"

만약 네가 앞말의 교체기를 이해할 수 있다면, 이때의 생성기도 이해하기 쉽다.이것은 yield 키워드를 통해 함수의 실행을 끊거나 멈추는 것으로 이해할 수 있습니다.그것의 외부는 다음 yield를 만나거나 함수가 실행될 때까지, 그것의 yield를 호출하여 함수를 계속 실행합니다. 사실은next 방법이 실행된 후에 끊은 곳입니다. 그리고 되돌아오는 데이터를 얻을 수 있습니다.
그래서 생성기는 이로써 일시 정지 (실행 유예) 할 수 있는 함수로 이해할 수 있다.

생성기의 특징과 자체 테이프 방법


next는 매개 변수를 전달해야 합니까?
다음은 예를 하나 더 보겠습니다.
 function * say(x) {
     let y = 2 * (yield x)
     let z = yield (y / 3)
     return x+y+z
 }
let it = say(5)
console.log(it.next());//{value: 5, done: false}
console.log(it.next());//{value: NaN, done: false}
console.log(it.next());//{value: NaN, done: false}

yield 표현식 자체에 반환 값이 없거나, 항상 undefined를 반환합니다.undefined + 숫자는 NaN을 되돌려줍니다. next 방법은 매개 변수를 가져올 수 있습니다. 이 매개 변수는 이전 yield 표현식의 반환 값으로 간주됩니다.
다음은next에 인자를 전달합니다.
function* say(x) {
   let y = 2 * (yield x)
   let z = yield(y / 3)
   console.log(x,y,z);//9,18,5
   return x + y + z
}
let it = say(6)
console.log(it.next());//{value: 6, done: false}
console.log(it.next(9));//{value: 6, done: false}
console.log(it.next(5));//{value: 29, done: true}

여기는 진지하게 이해해야 합니다. 첫 번째 it.next () 시 되돌아오는 값은 (yield x) 의 값이고, say(6) 전참은 6이기 때문에 첫 번째 next () 되돌아오는 결과는 x:6 두 번째 it입니다.nxt(9)시 전송 값은 9입니다. 이 매개 변수는 nxt의 반환 값을 바꾸어 계산합니다. 따라서 결과는 x:9,y= 2 * x =18, 반환z = y/3 = 6, 최종 반환 결과는 z:6입니다.세 번째 it.next(5) 시 전가는 5이고 이전 반환 결과 z:6을 변경하면 z=5, 전체 x:9, y:18, z:5이기 때문에xyz의 합은 29입니다.
넥스트 방법의 매개 변수는 이전 yield 표현식의 반환 값을 표시하기 때문에 넥스트 방법을 처음 사용할 때 매개 변수를 전달하는 것은 무효입니다.V8 엔진은 처음 넥스트 방법을 사용할 때의 매개 변수를 무시합니다. 두 번째 넥스트 방법을 사용할 때부터만 매개 변수가 유효합니다.
for of 순환을 사용할 때next () 를 호출할 필요가 없습니다
for...of 순환은Generator 함수가 실행될 때 생성된 Iterator 대상을 자동으로 훑어볼 수 있으며,next 방법을 사용할 필요가 없습니다.생성기로 피보나치 수열을 완성합니다.
function* fibonacci() {
  let [prev, curr] = [0, 1];
  for (curr=1;curr;[prev, curr] = [curr, prev + curr]) {
    yield curr;
  }
}
for (let n of fibonacci()) {
  if (n > 1000) break;
  console.log(n);
}
yiled* 표현식
ES6는 하나의 Generator 함수에서 다른 Generator 함수를 실행하는 데 사용되는 yield* 표현식을 제공합니다.
function* inner() {
  yield 'hello!';
}

function* outer1() {
  yield 'open';
  yield inner();
  yield 'close';
}

var gen = outer1()
gen.next().value // "open"
gen.next().value //  
gen.next().value // "close"

function* outer2() {
  yield 'open'
  yield* inner()
  yield 'close'
}

var gen = outer2()
gen.next().value // "open"
gen.next().value // "hello!"
gen.next().value // "close"

만약 yield 표현식 뒤에 역행기 대상이 있다면, yield 표현식 뒤에 별표를 붙여서 역행기 대상이 되돌아왔다는 것을 표시해야 한다.이것은 yield* 표현식이라고 불린다.

생성기의 실례 방법


throw
Generator는 함수체 밖에서 오류를 던져 함수체에서 포획합니다.예:
function* gen(x){
  try {
    var y = yield x + 2;
  } catch (e){
    console.log(e);
  }
  return y;
}

var g = gen(1);
g.next();
g.throw(' ');

return
주어진 값을 되돌려주고 생성기를 종료합니다.예:
function *gen2(){
    yield 1;
    yield 2;
    yield 3;
}
let g2 = gen1();
g2.next();//{value:1,done:false}
g2.return();//{value:undefined,done:true}
g2.next();//{value:undefined.done:true}

캐리어 대상 g2가return 방법을 호출하면Generator 함수의 캐리어가 종료됩니다. 값을 되돌려주는done 속성은true이고 나중에next 방법을 호출하면done 속성은true로 되돌아옵니다.return 방법이 호출될 때 매개 변수를 제공하지 않으면 값의value 속성은undefined입니다.

생성기의 실용적인 장면?


Generator는 함수 실행을 중지하고 임의의 표현식 값을 되돌려줍니다.이런 특징 때문에Generator는 다양한 응용 장면을 가지고 있다.
비동기 코드의 동기화를 실현하다
때때로 우리는 비동기적인 조작에 동기화 행위를 추가하기를 희망한다.예를 들어 나는 네 마디를 인쇄하고 싶지만 한 마디 한 마디를 토대로 출력을 2초 늦춘다.코드는 다음과 같습니다. 타이머 사용:
setTimeout(function(){
    console.log("first");
    setTimeout(function(){
        console.log("second");
        setTimeout(function(){
            console.log("third");
            setTimeout(function(){
                console.log("fourth");
            },2000);
        },2000);
    },2000);
},2000);

리셋 함수를 사용하여 비동기화를 실현한다. 당신은 앞의 비동기 조작이 완성된 후에 다음 동작을 하고 싶다. 그러면 그 리셋 함수에서만 진행할 수 있다. 이렇게 하면 갈수록 많아지고 코드가 갈수록 복잡해진다. 속칭'리셋 지옥'이라고 부른다.
그러면 promise로 이 기능을 최적화합니다.
let timeout = function(time){
    return new Promise(function(resolve,reject){
        setTimeout(function(){
            resolve();
        },time);
    });
}
console.log("go on");
timeout(2000).then(function(){
    console.log("first");
    return timeout(2000);
}).then(function(){
    console.log("second");
    return timeout(2000);
}).then(function(){
    console.log("third");
    return timeout(2000);
}).then(function(){
    console.log("fourth");
    return timeout(2000);
});

Promise는 ES6에서 제공하는 새로운 비동기식 프로그래밍 솔루션이지만 문제가 있습니다.예를 들어 코드는 새로운 방법의 출현으로 인해 감소하지 않고 오히려 더욱 복잡해졌으며 이해의 어려움도 커졌다.
Generators를 사용하여 이 기능을 구현하는 방법:
// Promise , 
function sleep(time, data) {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve(console.log(data))
                }, time);
            })
        }
// 
function* timeout(time) {
            let sleep1 = yield sleep(2000, 'first')
            let sleep2 = yield sleep(2000, 'second')
            let sleep3 = yield sleep(2000, 'third')
            let sleep4 = yield sleep(2000, 'fourth')
            return console.log('go on');
        }
//  
let it = timeout();
it.next().value.then(() => {
            it.next().value.then(() => {
                it.next().value.then(() => {
                    it.next().value.then(() => {
                        it.next()
                    })
                })
            })
});
//  
function run(gen) {
            var g = gen()
            function next(data) {
                var result = g.next(data) //{value: Promise, done: false}
                if (result.data) return result.value // done:true, 
                result.value.then(data => next(data))// , next() 
            }
            next()
}
run(timeout)

상기 코드는 생성기 +promise를 사용하여 시간 2초 인쇄 기능을 완성했습니다. 수동으로 실행하는 것은 사실then 방법으로 층층이 리셋 함수를 추가하는 것입니다.자동 실행은Generator 함수가 마지막 단계까지 실행되지 않으면next 함수는 자신을 호출하여 한 번에 자동 실행을 실현합니다.
이렇게 하면 생성기에서 동기화 코드를 쓰는 것처럼 비동기적인 조작을 실현할 수 있다.
그러나 생성기라는 방식은 외부의 집행기를 작성해야 하는데 집행기의 코드는 쓰기가 간단하지 않다.외부 모듈을 사용하여 자동으로 실행할 수 있는 것 외에 우리는 es7에서 생성기를 바탕으로 이루어진 문법 설탕: async와await를 배울 수 있다
그럼 우리 async와await가 어떻게 이 기능을 실현하는지 볼까요?
 function sleep(time, data) {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve(console.log(data))
                }, time);
            })
        }
  async function timeout() {
       let sleep1 = await sleep(2000, 'first')
       let sleep2 = await sleep(2000, 'second')
       let sleep3 = await sleep(2000, 'third')
       let sleep4 = await sleep(2000, 'fourth')
       return console.log('go on');
  }
 timeout()

이렇게 동기화처럼 비동기 코드를 쓰는 것은 간단하게 이루어졌다. 소리를 지르며 윽박지르다!그러나 es7의 문법이기 때문에 호환성을 고려해야 한다.
ES7에 async 함수를 추가하여 비동기화를 처리합니다.이것은 사실상 생성기의 문법 설탕일 뿐이다. 외부 집행기의 코드를 간소화하고 await를 이용하여 yield를 대체하고 async를 이용하여 생성기의 (*)호를 대체한다.
첫 문장은 await를 썼다.그것은 이전의 yield를 대체했다.뒤에 Promise 객체를 따라가야 합니다.다음 인쇄문은 위의 비동기식 작업이 끝난 후에 실행됩니다.외부 호출은 정상적인 함수 호출과 같지만, 그 실현 원리는 생성기와 유사하다.async 키워드가 있기 때문에 외부에 해당하는 실행기가 있고 비동기적인 작업이 끝난 후에 리셋 함수를 실행합니다.단지 이 모든 것이 숨겨졌을 뿐, JS 엔진이 우리를 도와 완성했다.우리가 해야 할 일은 키워드를 붙여서 함수에서await를 사용하여 비동기적인 조작을 하는 것이다.이렇게 하면 비동기식 조작을 크게 간소화할 수 있다.
첨부: 자동 실행 플러그인 – co 모듈
Generators는 동기화처럼 비동기 코드를 쓸 수 있지만 실행하는 과정이 마음에 들지 않기 때문에 플러그인을 빌려서:co모듈을 완성할 수 있습니다.
위의 예에서 알 수 있듯이generator 함수체는 다음 실행next () 까지 yield 문장에 멈출 수 있습니다.co 모듈의 사고방식은generator의 이 특성을 이용하여 비동기 조작을 yield 뒤에 따라 비동기 조작이 완성되고 결과를 되돌린 후에 다음next()를 터치하는 것이다.

결어


이상은es6의Generators(생성기)에 대해 완전하게 이해한 셈이며, 비동기적인 처리 방법과 각자의 장단점을 비교하여 다른 편을 보십시오. 자바스크립트 비동기적인 프로그래밍은 무엇입니까?비동기 프로그래밍에는 어떤 해결 방안이 있습니까?참고:https://www.cnblogs.com/jaxu/p/6372809.html http://es6.ruanyifeng.com/#docs/generator
만약 본문이 당신에게 도움이 된다면 저에게 좋아요를 눌러주시고 콜을 하는 것을 잊지 마세요.

좋은 웹페이지 즐겨찾기