JavaScript에서 yield 키워드는 무엇입니까?


238

JavaScript에서 "yield"키워드에 대해 들었지만 그에 대한 문서가 매우 부족했습니다. 누군가 나에게 사용법과 용도를 설명 할 수 있습니까?


아마도 ' Yield'bytes.com
ant

4
MDN에 설명되어 있지만 파이어 폭스에서만 작동한다고 생각합니까? 얼마나 휴대가 편리합니까? Chrome 또는 node.js 에서이 방법을 사용할 수 있습니까? PD : 죄송합니다. Javascript v1.7 + 이므로 지원을 찾을 때 살펴볼 수있는 속성입니다.
Trylks

1
@Trylks : 발전기는 v0.11.2 이후 노드에서 사용할 수 있습니다
야누스 Troelsen

그러나 @JanusTroelsen은 깃발 뒤에 만 있습니다. 그들은 ioJS에서 기본적으로 지원됩니다
Dan Pantry

답변:


86

MDN 문서는 IMO, 아주 좋은 것입니다.

yield 키워드를 포함하는 함수는 생성기입니다. 호출하면 형식 매개 변수는 실제 인수에 바인딩되지만 본문은 실제로 평가되지 않습니다. 대신 생성기-반복기가 리턴됩니다. generator-iterator의 next () 메소드를 호출 할 때마다 반복 알고리즘을 통해 다른 패스를 수행합니다. 각 단계의 값은 yield 키워드로 지정된 값입니다. yield를 생성기-반복기 버전의 리턴으로 생각하여 알고리즘의 각 반복 사이의 경계를 나타냅니다. next ()를 호출 할 때마다 생성기 코드는 수율에 따라 명령문에서 재개됩니다.


2
@NicolasBarbulesco 당신이 MDN 문서를 클릭하면 아주 분명한 예가 있습니다.
매트 볼

@MattBall-다음과 같이 PI 용 자바 스크립트로서의 함수이면 충분합니다. function * PI {PI = ((Math.SQRT8;) / 9801;); }-또는이 PI 계산을 위해 이미 JavaScript로 구현 된 함수가 있습니까?
dschinn1001

4
여기 MDN을 인용하는 요점은 무엇입니까? MDN에서 누구나 읽을 수 있다고 생각합니다. 자세한 내용은 davidwalsh.name/promises 를 방문하십시오 .
Ejaz Karim

20
(a) 질문자가 전화를 할 때 "매우 불량한 문서"의 사본이고 (b) 아무런 도움이 없다고 말할 때이 방법은 ~ 80 개의 공짜를 얻었습니까? 아래에 더 나은 답변이 있습니다.
WWW-0av 닷컴

4
누군가가 설명을 요청하면 문서를 붙여 넣는 것만으로도 전혀 쓸모가 없습니다. 요청은 문서에서 이미 검색했지만 이해하지 못했음을 의미합니다.
Diego

205

늦게 대답하면 아마 모든 사람들이 yield지금 알고 있지만 더 나은 문서가 나왔습니다.

공식 하모니 표준을 위해 James Long 의 "Javascript 's Future : Generators" 에서 예를 채택 :

function * foo(x) {
    while (true) {
        x = x * 2;
        yield x;
    }
}

"foo를 호출하면 다음 메소드가있는 Generator 객체를 다시 얻게됩니다."

var g = foo(2);
g.next(); // -> 4
g.next(); // -> 8
g.next(); // -> 16

그래서 yield일종의 같은 것입니다 return: 당신은 무언가를 되 찾습니다. return x의 값을 반환 x하지만 yield x다음 값으로 반복 할 수있는 메서드를 제공하는 함수를 반환합니다. 반복 중에 중단 할 수 있는 메모리 집약적 인 프로 시저 가있는 경우 유용합니다 .


13
도움,하지만 난 당신의 생각 function* foo(x){
라나 깊은

9
@RanaDeep : 함수 구문이 확장되어 선택적 * 토큰 을 추가합니다 . 필요 여부는 귀국하는 미래의 종류에 따라 다릅니다. 세부 사항은 길다 : GvR은 파이썬 구현에 대해 설명 하고 자바 스크립트 구현이 모델링된다. 경우에 따라 사용하는 function *것보다 약간 더 많은 오버 헤드가 있지만 사용하는 것이 항상 옳 function습니다 yield.
주교

1
@ Ajedi32 그렇습니다. 조화 사이의 상관 관계를 표준화 function *하고 yield, 상기 인용 된 에러 ( "수율이나 수율 *식이 아닌 함수 발생기에서 발생하는 경우, 초기 오류가 발생")을 추가했다. 그러나 Firefox의 원래 Javascript 1.7 구현 에는을 요구하지 않았습니다* . 이에 따라 답변이 업데이트되었습니다. 감사!
감독

3
@MuhammadUmer Js는 마침내 실제로 사용할 수있는 언어가되었습니다. 이것을 진화라고합니다.
Lukas Liesis

1
예제는 유용하지만 함수 란 무엇입니까?
Diego

66

정말 간단합니다. 이것이 작동하는 방식입니다

  • yield키워드는 단순히 비동기식 으로 언제든지 함수 를 일시 중지재개 하는 데 도움이됩니다. .
  • 또한 생성기 함수 에서 반환 하는 데 도움이됩니다 .

이 간단한 생성기 함수를 사용하십시오.

function* process() {
    console.log('Start process 1');
    console.log('Pause process2 until call next()');

    yield;

    console.log('Resumed process2');
    console.log('Pause process3 until call next()');

    let parms = yield {age: 12};
    console.log("Passed by final process next(90): " + parms);

    console.log('Resumed process3');
    console.log('End of the process function');
}

let _process = process ();

당신이 호출 할 때까지 () _process.next실 거예요 실행] 처음 2 줄 의 코드를 다음 첫 번째 수율이 됩니다 일시 정지 기능을. 다음 일시 정지 지점 ( yield 키워드 ) 까지 함수 를 재개 하려면 _process.next () 를 호출해야합니다 .

단일 함수 내에서 자바 스크립트 디버거 의 여러 수율중단 점 이라고 생각할 수 있습니다 . 다음 중단 점을 탐색하도록 지시 할 때까지 코드 블록을 실행하지 않습니다. ( 참고 : 전체 응용 프로그램을 차단하지 않고)

그러나 yield는 이러한 일시 정지 및 재개 동작을 수행하지만 이전 함수에 따라 값을 생성하지 않은 결과반환 할 수 있습니다 {value: any, done: boolean}. 이전 출력을 살펴보면 undefined{ value: undefined, done: false } 값과 동일하게 표시됩니다 .

yield 키워드를 파헤칩니다. 선택적으로 expression 을 추가 하고 기본 선택적 값을 지정하도록 설정할 수 있습니다 . (공식 문서 구문)

[rv] = yield [expression];

expression : 생성기 함수에서 반환 할 값

yield any;
yield {age: 12};

rv : 생성기 next () 메소드에 전달 된 선택적 값을 리턴합니다.

이 메커니즘을 사용하여 process () 함수에 매개 변수를 전달하여 다른 항복 파트를 실행할 수 있습니다.

let val = yield 99; 

_process.next(10);
now the val will be 10 

지금 사용해보십시오

사용법

  • 게으른 평가
  • 무한 시퀀스
  • 비동기 제어 흐름

참고 문헌 :


54

Nick Sotiros의 답변을 단순화 / 정교하게 만드는 방법은 훌륭하다고 생각합니다 yield.

내 의견으로는, 사용의 가장 큰 장점 yield 코드에서 볼 수있는 모든 중첩 콜백 문제를 제거한다는 것입니다. 처음에는 방법을 알기가 어렵 기 때문에이 답변을 작성하기로 결정했습니다 (나 자신과 다른 사람들을 위해)!

이를 수행하는 방법은 필요한 것을 얻을 때까지 자발적으로 중지 / 일시 정지 할 수있는 기능인 공동 루틴 아이디어를 도입하는 것입니다. 자바 스크립트에서 이는로 표시됩니다 function*. function*기능 만 사용할 수 있습니다 yield.

다음은 일반적인 자바 스크립트입니다.

loadFromDB('query', function (err, result) {
  // Do something with the result or handle the error
})

이제 모든 코드 (이 loadFromDB호출 을 기다려야 함 ) 가보기 흉한 콜백 안에 있어야하기 때문에 이것은 복잡합니다 . 몇 가지 이유로 나쁘다 ...

  • 모든 코드는 한 수준으로 들여 쓰기됩니다
  • 당신은 당신이 })어디서나 추적 해야하는 이 끝 이있다
  • 이 모든 추가 function (err, result) 전문 용어
  • 값을 할당하기 위해이 작업을 수행하고 있음을 정확히 알 수는 없습니다. result

반면 에을 사용하면 멋진 공동 루틴 프레임 워크를 사용하여 한 줄yield 로이 모든 작업을 수행 할 수 있습니다 .

function* main() {
  var result = yield loadFromDB('query')
}

따라서 이제 주요 함수는 변수와로드 될 때까지 기다려야 할 때 필요한 곳에서 생성됩니다. 그러나 이제 이것을 실행하려면 일반 (비 병렬 함수) 을 호출해야합니다 . 간단한 공동 루틴 프레임 워크로이 문제를 해결할 수 있으므로 다음을 실행하면됩니다.

start(main())

그리고 시작이 정의됩니다 (Nick Sotiro의 답변에서)

function start(routine, data) {
    result = routine.next(data);
    if(!result.done) {
        result.value(function(err, data) {
            if(err) routine.throw(err); // continue next iteration of routine with an exception
            else start(routine, data);  // continue next iteration of routine normally
        });
    }
}

이제 더 읽기 쉽고 삭제하기 쉽고 들여 쓰기, 함수 등으로 바이올린을 칠 필요가없는 아름다운 코드를 가질 수 있습니다.

이 예제에서는 yield실제로 콜백이있는 함수 앞에 넣을 수있는 키워드 일뿐입니다.

function* main() {
  console.log(yield function(cb) { cb(null, "Hello World") })
}

"Hello World"를 인쇄합니다. 따라서 실제로 yield동일한 함수 서명 (cb없이)을 만들고을 반환 하여 콜백 함수를 실제로 사용할 수 있습니다 function (cb) {}.

function yieldAsyncFunc(arg1, arg2) {
  return function (cb) {
    realAsyncFunc(arg1, arg2, cb)
  }
}

이 지식 으로 삭제하기 쉬운 더 읽기 쉽고 깔끔한 코드를 작성할 수 있기를 바랍니다 .


a function*는 수율이없는 정규 함수입니까?
Abdul

나는 그것이 수확량 function *포함 하는 함수 라는 것을 의미한다고 생각합니다 . 제너레이터라고하는 특별한 기능입니다.
Leander

7
이미 yield모든 곳에서 사용하는 사람들에게는 이것이 콜백보다 더 의미가 있다고 확신하지만 콜백보다 더 읽기 쉬운 방법은 알 수 없습니다.
palswim

그 기사는 이해하기 어렵다
Martian2049

18

완전한 대답을 제공하려면 : yield과 비슷 return하지만 발전기에서 작동합니다 .

일반적으로 주어진 예제와 같이 다음과 같이 작동합니다.

function *squareGen(x) {
    var i;
    for (i = 0; i < x; i++) {
        yield i*i;
    }
}

var gen = squareGen(3);

console.log(gen.next().value); // prints 0
console.log(gen.next().value); // prints 1
console.log(gen.next().value); // prints 4

그러나 yield 키워드의 두 번째 목적도 있습니다. 생성기에 값을 보내는 데 사용할 수 있습니다.

명확히하기 위해 작은 예 :

function *sendStuff() {
    y = yield (0);
    yield y*y;
}

var gen = sendStuff();

console.log(gen.next().value); // prints 0
console.log(gen.next(2).value); // prints 4

이 값 2은에 할당 된 대로 y첫 번째 수확량 (으로 반환 된 0) 에서 중지 된 후 생성기로 전송하여 작동합니다 .

이것은 우리에게 정말 펑키 한 것들을 가능하게합니다. (코 루틴 조회)


16

반복자 생성기에 사용됩니다. 기본적으로 절차 코드를 사용하여 (잠재적으로 무한한) 시퀀스를 만들 수 있습니다. Mozilla 설명서를 참조하십시오 .


6

yield 코 루틴 프레임 워크를 사용하여 콜백 지옥을 제거하는 데 사용할 수도 있습니다.

function start(routine, data) {
    result = routine.next(data);
    if(!result.done) {
        result.value(function(err, data) {
            if(err) routine.throw(err); // continue next iteration of routine with an exception
            else start(routine, data);  // continue next iteration of routine normally
        });
    }
}

// with nodejs as 'node --harmony'
fs = require('fs');
function read(path) {
    return function(callback) { fs.readFile(path, {encoding:'utf8'}, callback); };
}

function* routine() {
    text = yield read('/path/to/some/file.txt');
    console.log(text);
}

// with mdn javascript 1.7
http.get = function(url) {
    return function(callback) { 
        // make xhr request object, 
        // use callback(null, resonseText) on status 200,
        // or callback(responseText) on status 500
    };
};

function* routine() {
    text = yield http.get('/path/to/some/file.txt');
    console.log(text);
}

// invoked as.., on both mdn and nodejs

start(routine());

4

yield 키워드를 사용하는 피보나치 시퀀스 생성기.

function* fibbonaci(){
    var a = -1, b = 1, c;
    while(1){
        c = a + b;
        a = b;
        b = c;
        yield c;
    }   
}

var fibonacciGenerator = fibbonaci();
fibonacciGenerator.next().value; // 0 
fibonacciGenerator.next().value; // 1
fibonacciGenerator.next().value; // 1
fibonacciGenerator.next().value; // 2 

4

Yeild 자바 스크립트 함수의 키워드는 생성기를 만듭니다.

자바 스크립트에서 제너레이터 란 무엇입니까?

제너레이터는 단일 값 대신 일련의 결과를 생성하는 함수입니다. 즉 일련의 값을 생성합니다.

의미 생성기는 도움말 반복자와 비동기식으로 작업하는 데 도움이됩니다. 이제 해킹 반복자가 무엇입니까? 정말?

반복자는 한 번에 하나의 항목에 액세스 할 수있는 수단입니다.

한 번에 하나씩 항목에 액세스하는 데 반복자가 도움이되는 곳부터? 생성기 함수를 통해 항목에 액세스하는 데 도움이됩니다.

생성기 함수는 yeild키워드 를 사용하는 함수이며 , 키워드를 생성하면 함수 실행을 일시 중지하고 다시 시작하는 데 도움이됩니다.

여기 빠른 예가 있습니다

function *getMeDrink() {

    let question1 = yield 'soda or beer' // execution will pause here because of yield

 if (question1 == 'soda') {

            return 'here you get your soda'

    }

    if (question1 == 'beer') {

        let question2 = yield 'Whats your age' // execution will pause here because of yield

        if (question2 > 18) {

            return "ok you are eligible for it"

        } else {

            return 'Shhhh!!!!'

        }
    }
}


let _getMeDrink = getMeDrink() // initialize it

_getMeDrink.next().value  // "soda or beer"

_getMeDrink.next('beer').value  // "Whats your age"

_getMeDrink.next('20').value  // "ok you are eligible for it"

_getMeDrink.next().value // undefined

무슨 일인지 설명해 보자

yeild키워드 에서 실행이 일시 중지되었으며 yield반복자의 도움을 받아 먼저 액세스 할 수 있습니다..next()

이것은 yield한 번에 하나씩 모든 키워드를 반복 한 다음 yield간단한 단어로 키워드 가 더 이상 남아 있지 않으면 undefined를 반환합니다. yield키워드는 중단 될 때마다 함수가 일시 중지되고 반복자를 사용하여 호출 할 때만 다시 시작되는 중단 점 이라고 말할 수 있습니다.

우리의 경우 : _getMeDrink.next()이것은 함수의 각 중단 점에 액세스하는 데 도움이되는 반복기의 예입니다.

발전기의 예 : async/await

당신이 당신의 구현을 볼 경우 작동 하는 데 사용 async/await 됩니다 참조generator functions & promisesasync/await

어떤 제안도 환영합니다.


3

비동기 자바 스크립트 호출 간의 종속성

수확량을 사용하는 방법에 대한 또 다른 좋은 예입니다.

function request(url) {
  axios.get(url).then((reponse) => {
    it.next(response);
  })
}

function* main() {
  const result1 = yield request('http://some.api.com' );
  const result2 = yield request('http://some.otherapi?id=' + result1.id );
  console.log('Your response is: ' + result2.value);
}

var it = main();
it.next()


0

생산량에 대해 배우기 전에 발전기에 대해 알아야합니다. 생성기는 function*구문을 사용하여 작성됩니다 . 생성기 함수는 코드를 실행하지 않고 생성기라는 반복기 유형을 반환합니다. next메소드를 사용하여 값을 지정 하면 생성기 함수는 yield 키워드를 발견 할 때까지 계속 실행됩니다. 를 사용 yield하면 하나는 값이고 다른 하나는 수행됩니다 (부울). 값은 배열, 객체 등이 될 수 있습니다.


0

간단한 예 :

const strArr = ["red", "green", "blue", "black"];

const strGen = function*() {
    for(let str of strArr) {
        yield str;
    }
};

let gen = strGen();

for (let i = 0; i < 5; i++) {
    console.log(gen.next())
}

//prints: {value: "red", done: false} -> 5 times with different colors, if you try it again as below:

console.log(gen.next());

//prints: {value: undefined, done: true}

0

또한 yield 키워드를 이해하려고합니다. 내 현재 이해를 바탕으로 generator에서 yield 키워드는 CPU 컨텍스트 스위치처럼 작동합니다. yield 문이 실행되면 모든 상태 (예 : 로컬 변수)가 저장됩니다.

이 외에도 직접적인 결과 객체가 {value : 0, done : false}와 같이 호출자에게 반환됩니다. 호출자는이 결과 객체를 사용하여 next ()를 호출하여 생성기를 다시 '깨 울지'여부를 결정할 수 있습니다 (next () 호출은 실행을 반복 함).

또 다른 중요한 점은 값을 로컬 변수로 설정할 수 있다는 것입니다. 이 값은 생성기를 '웨이크 업'할 때 'next ()'호출자가 전달할 수 있습니다. 예를 들어 다음과 같이 it.next ( 'valueToPass') : "resultValue = yield slowQuery (1);" 다음 실행을 깨울 때와 마찬가지로 호출자는 실행에 일부 실행 결과를 로컬 변수에 주입하여 실행할 수 있습니다. 따라서이 실행에는 두 가지 상태가 있습니다.

  1. 마지막 실행에서 저장 한 컨텍스트

  2. 이 실행 트리거에 의해 주입 된 값.

따라서이 기능을 사용하면 생성기가 여러 비동기 작업을 정렬 할 수 있습니다. 첫 번째 비동기 쿼리의 결과는 로컬 변수 (위의 예에서 resultValue)를 설정하여 두 번째 비동기 쿼리에 전달됩니다. 두 번째 비동기 쿼리는 첫 번째 비동기 쿼리의 응답에 의해서만 트리거 될 수 있습니다. 그런 다음 두 번째 비동기 쿼리는 로컬 변수 값을 확인하여 다음 단계를 결정합니다. 로컬 변수는 첫 번째 쿼리의 응답에서 주입 된 값이기 때문입니다.

비동기 쿼리의 어려움은 다음과 같습니다.

  1. 콜백 지옥

  2. 콜백에서 매개 변수로 전달하지 않으면 컨텍스트가 손실됩니다.

수율과 생성기는 두 가지 모두에 도움이 될 수 있습니다.

수율과 생성기가 없으면 여러 비동기 쿼리를 정렬하려면 읽기 및 유지 관리가 쉽지 않은 컨텍스트로 매개 변수를 사용하는 중첩 콜백이 필요합니다.

다음은 nodejs로 실행되는 체인 비동기 쿼리 예제입니다.

const axios = require('axios');

function slowQuery(url) {        
    axios.get(url)
    .then(function (response) {
            it.next(1);
    })
    .catch(function (error) {
            it.next(0);
    })
}

function* myGen(i=0) {
    let queryResult = 0;

    console.log("query1", queryResult);
    queryResult = yield slowQuery('https://google.com');


    if(queryResult == 1) {
        console.log("query2", queryResult);
        //change it to the correct url and run again.
        queryResult = yield slowQuery('https://1111111111google.com');
    }

    if(queryResult == 1) {
        console.log("query3", queryResult);
        queryResult =  yield slowQuery('https://google.com');
    } else {
        console.log("query4", queryResult);
        queryResult = yield slowQuery('https://google.com');
    }
}

console.log("+++++++++++start+++++++++++");
let it = myGen();
let result = it.next();
console.log("+++++++++++end+++++++++++");

아래는 실행 결과입니다.

+++++++++++ start +++++++++++

query1 0

+++++++++++ end +++++++++++

query2 1

query4 0

아래 상태 패턴은 위의 예와 비슷한 작업을 수행 할 수 있습니다.

const axios = require('axios');

function slowQuery(url) {
    axios.get(url)
        .then(function (response) {
            sm.next(1);
        })
        .catch(function (error) {
            sm.next(0);
        })
}

class StateMachine {
        constructor () {
            this.handler = handlerA;
            this.next = (result = 1) => this.handler(this, result);
        }
}

const handlerA = (sm, result) => {
                                    const queryResult = result; //similar with generator injection
                                    console.log("query1", queryResult);
                                    slowQuery('https://google.com');
                                    sm.handler = handlerB; //similar with yield;
                                };

const handlerB = (sm, result) => {
                                    const queryResult = result; //similar with generator injection
                                    if(queryResult == 1) {
                                        console.log("query2", queryResult);
                                        slowQuery('https://1111111111google.com');
                                    }
                                    sm.handler = handlerC; //similar with yield;
                                };

const handlerC = (sm, result) => {
                                    const queryResult = result; //similar with generator injection;
                                    if (result == 1 ) {
                                        console.log("query3", queryResult);
                                        slowQuery('https://google.com');
                                    } else {
                                        console.log("query4", queryResult);
                                        slowQuery('https://google.com');
                                    }
                                    sm.handler = handlerEnd; //similar with yield;
                                };

const handlerEnd = (sm, result) => {};

console.log("+++++++++++start+++++++++++");
const sm = new StateMachine();
sm.next();
console.log("+++++++++++end+++++++++++");

다음은 실행 결과입니다.

+++++++++++ start +++++++++++

query1 0

+++++++++++ end +++++++++++

query2 1

query4 0


0

생성기를 반복하는 데 유용한 'x of generator'구문을 잊지 마십시오. next () 함수를 전혀 사용할 필요가 없습니다.

function* square(x){
    for(i=0;i<100;i++){
        x = x * 2;
        yield x;        
    }   
}

var gen = square(2);
for(x of gen){
   console.log(x);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.