생성기를 사용한 비동기 / 대기 및 ES6 수율의 차이점


82

저는이 환상적인 기사« Generators »를 읽고 있었는데 ,이 함수는 제너레이터 함수를 처리하기위한 도우미 함수 인이 함수를 명확하게 강조합니다.

function async(makeGenerator){
  return function () {
    var generator = makeGenerator.apply(this, arguments);

    function handle(result){
      // result => { done: [Boolean], value: [Object] }
      if (result.done) return Promise.resolve(result.value);

      return Promise.resolve(result.value).then(function (res){
        return handle(generator.next(res));
      }, function (err){
        return handle(generator.throw(err));
      });
    }

    try {
      return handle(generator.next());
    } catch (ex) {
      return Promise.reject(ex);
    }
  }
}

나는 async키워드가 async/ 로 구현 되는 방식과 다소 비슷하다고 가정 await합니다. 그렇다면 문제는 await키워드와 키워드 의 차이점이 도대체 무엇 yield일까요? 않는 await반면 항상 약속에 뭔가를 돌려 yield그러한 보증을하지 않습니다? 그것은 내 최선의 추측입니다!

당신은 또한 방법을 볼 수 있습니다 async/ await유사하다 yield그는 '부활'기능을 설명하는이 문서의 발전기 기능 비동기 ES7을 .


1
비동기 함수-> 코 루틴. 생성기-> 내부 반복 메커니즘을 관리하기 위해 코 루틴을 사용하는 반복기. await를 일시 중단 코 루틴, 수율은 코 루틴에서 결과를 반환하면서있는 일부 발전기 사용
데이비드 하임

1
async/awaitES7의 일부가 아닙니다. 태그 설명을 읽으십시오.
펠릭스 클링

@ 데이비드 하임, 그래하지만 비동기 await를 그들은 서로 다른되지 않도록 발전기의 상단에 내장되어 있습니다
알렉산더 밀스를

답변:


46

yield의 빌딩 블록으로 간주 될 수 있습니다 await. yield주어진 값을 받아 호출자에게 전달합니다. 그런 다음 호출자는 해당 값 (1)으로 원하는 모든 작업을 수행 할 수 있습니다. 나중에 호출자는 (2) 표현식 generator.next()의 결과가되는 생성자 (를 통해 )에 값을 다시 제공 yield하거나 yield(3) 표현식에 의해 throw되는 것처럼 보이는 오류를 제공 할 수 있습니다 .

async- await를 사용하는 것으로 간주 할 수 있습니다 yield. (즉, (1) 호출자에 async- await드라이버 - 당신이 게시 된 기능과 유사)와 유사한 알고리즘을 사용하여 약속의 값을 바꿈됩니다 new Promise(r => r(value)(참고, 하지 Promise.resolve ,하지만 그건 큰 문제가 아니다). 그런 다음 약속이 해결 될 때까지 기다립니다. 충족되면 충족 된 값을 (2)로 다시 전달합니다. 거부하면 (3)에서 오류로 거부 사유를 던집니다.

따라서 async- 의 유용성은 산출 된 값을 약속으로 풀고 해결 된 값을 다시 전달하여 함수가 최종 값을 반환 할 때까지 반복 await하는 데 사용 yield하는 이 기계입니다 .


이 인수와 모순되는 이 답변 stackoverflow.com/a/39384160/3933557 을 확인하십시오 . async-await는 yield와 비슷해 보이지만 내부적으로 promise 체인을 사용합니다. "async-await는 yield를 사용하는 것으로 간주 될 수 있습니다."라는 좋은 리소스가 있으면 공유하십시오.
Samarendra

나는 당신이 어떻게 그 대답을 "이 주장과 모순되는"것으로 받아들이고 있는지 잘 모르겠습니다. 왜냐하면 그것은이 대답과 같은 것을 말하고 있기 때문입니다. > 그동안 Babel과 같은 트랜스 파일러를 사용하면 async / await를 작성하고 코드를 생성기로 변환 할 수 있습니다.
Arnavion

그것은 babel이 발전기로 변환한다고 말하고 있지만 당신이 말하는 것은 "yield는 await의 빌딩 블록으로 간주 될 수있다"와 "async-await는 yield를 사용하는 것으로 간주 될 수 있습니다."입니다. 내 이해에 맞지 않습니다 (수정 대상). async-await는 해당 답변에서 언급 한 약속 체인을 내부적으로 사용합니다. 내가 놓친 것이 있는지 이해하고 싶습니다. 이것에 대한 귀하의 생각을 공유해 주시겠습니까?
Samarendra

이 답변은 전 세계의 모든 ES 엔진이 생성기를 사용하여 내부적으로 약속을 구현한다는 주장을하지 않습니다. 일부는; 일부는 그렇지 않을 수도 있습니다. 이것이 답이라는 질문과는 무관합니다. 그럼에도 불구하고 약속이 작동하는 방식은 발전기를 구동하는 특정 방법을 가진 발전기를 사용하여 이해할 수 있으며 이것이이 답변이 설명하는 것입니다.
Arnavion

43

글쎄요, async/ await와 발전기 사이에는 매우 밀접한 관계가 있다는 것이 밝혀졌습니다 . 그리고 저는 믿습니다 async/가 await항상 발전기에 건설 될 것입니다. Babel이 변환하는 방식을 살펴보면 async/ await:

Babel은 다음을 수행합니다.

this.it('is a test', async function () {

    const foo = await 3;
    const bar = await new Promise(resolve => resolve('7'));
    const baz = bar * foo;
    console.log(baz);

});

그리고 이것을 이렇게

function _asyncToGenerator(fn) {
    return function () {
        var gen = fn.apply(this, arguments);
        return new Promise(function (resolve, reject) {
            function step(key, arg) {
                try {
                    var info = gen[key](arg);
                    var value = info.value;
                } catch (error) {
                    reject(error);
                    return;
                }
                if (info.done) {
                    resolve(value);
                } else {
                    return Promise.resolve(value).then(function (value) {
                        return step("next", value);
                    }, function (err) {
                        return step("throw", err);
                    });
                }
            }

            return step("next");
        });
    };
}


this.it('is a test', _asyncToGenerator(function* () {   // << now it's a generator

    const foo = yield 3;    //  <<< now it's yield, not await
    const bar = yield new Promise(resolve => resolve(7));
    const baz = bar * foo;
    console.log(baz);

}));

당신은 수학을합니다.

이것은 async키워드가 단지 그 래퍼 함수 인 것처럼 보이게 하지만, 만약 그렇다면 await그냥으로 바뀌면 yield나중에 네이티브가 될 때 그림에 조금 더 많은 것이있을 것입니다.

이에 대한 자세한 설명은 https://www.promisejs.org/generators/ 에서 확인할 수 있습니다.


1
NodeJS에는 생성기없이 잠시 동안 네이티브 비동기 / 대기 기능이 있습니다. codeforgeek.com/2017/02/…
Bram

3
@Bram 네이티브 구현은 완전히 추상화 된 내부에서 생성기를 사용합니다.
Alexander Mills

3
나는 그렇게 생각하지 않는다. Async / await는 기본적으로 V8 엔진에서 구현됩니다. ES6 기능, async / await가 ES7 인 생성기. V8 엔진 5.5 릴리스 (Node에서 사용됨)의 일부였습니다 . v8project.blogspot.nl/2016/10/v8-release-55.html . ES7 async / await를 ES6 생성기로 트랜스 파일 할 수 있지만 NodeJS의 새 버전에서는 더 이상 필요하지 않으며 async / await의 성능이 생성기보다 더 나은 것 같습니다. medium.com/@markherhold/…
Bram

1
비동기 / await를 사용하는이 발전기는 일이해야 할 일
알렉산더 밀스

@AlexanderMills async / await가 내부적으로 생성기를 사용한다고 말하는 합법적 인 리소스를 공유해 주시겠습니까? 이 인수와 모순되는 this ans stackoverflow.com/a/39384160/3933557을 확인하십시오. Babel이 제너레이터를 사용한다고해서 내부적으로 유사하게 구현된다는 의미는 아닙니다. 이것에 대한 모든 생각
Samarendra

28

도대체 await키워드와 키워드 의 차이점은 무엇 yield입니까?

await키워드 만 사용되는 async function동안의 yield키워드가 발생에서만 사용되는 function*의. 그리고 그것들도 분명히 다릅니다. 하나는 약속을 반환하고 다른 하나는 생성자를 반환합니다.

않는 await반면 항상 약속에 뭔가를 돌려 yield그러한 보증을하지 않습니다?

예, 기다린 값을 await호출 Promise.resolve합니다.

yield 생성기 외부의 값을 산출합니다.


사소한 nit이지만 내 대답에서 언급했듯이 사양은 Promise.resolve (이전에 사용됨)를 사용하지 않으며 Promise 생성자에 의해 더 정확하게 표현되는 PromiseCapability :: resolve를 사용합니다.
Arnavion 2016 년

@Arnavion : 비동기 / 대기 사양이 직접 사용 Promise.resolve하는 것과 똑같은 new PromiseCapability(%Promise%)것을 사용합니다 Promise.resolve. 이해하는 것이 더 낫다고 생각했습니다 .
Bergi

1
Promise.resolve비동기에는없는 추가 "IsPromise == true? 그런 다음 동일한 값을 반환"단락이 있습니다. 즉, await pp이다 결의가 할 수있는 새로운 약속을 반환하는 약속 p, 반면이 Promise.resolve(p)반환을 p.
Arnavion

오, 나는 그것을 놓쳤다-나는 이것이 단지 Promise.cast일관된 이유로 더 이상 사용되지 않는다고 생각했다 . 그러나 그것은 중요하지 않습니다. 어쨌든 우리는 그 약속을 실제로 보지 못합니다.
Bergi

2
var r = await p; console.log(r);다음과 같이 변환되어야합니다 : p.then(console.log);, 다음과 같이 p생성 될 수 있습니다 : var p = new Promise(resolve => setTimeout(resolve, 1000, 42));, 따라서 "await calls Promise.resolve" 라고 말하는 것은 잘못되었습니다.를 호출 하는 'await'표현식에서 완전히 멀리 떨어진 다른 코드 Promise.resolve이므로 변환 된 await표현식 즉, Promise.then(console.log)호출되고 인쇄됩니다 42.
Dejavu

14

tl; dr

사용 async/ await발전기를 통해 시간의 99 %. 왜?

  1. async/ await는 가장 일반적인 promise 체인 워크 플로를 직접 대체하여 코드가 동기식 인 것처럼 선언 할 수 있도록하여 대폭 단순화합니다.

  2. 생성기는 서로 의존하고 결국 "완료"상태가되는 일련의 비동기 작업을 호출하는 사용 사례를 추상화합니다. 가장 간단한 예는 결국 마지막 세트를 반환하는 결과를 통해 페이징하는 것이지만 연속적으로 즉시 호출하지 않고 필요한 경우에만 페이지를 호출합니다.

  3. async/ await사실은 promise 작업을 더 쉽게하기 위해 생성기 위에 구축 된 추상화입니다.

Async / Await vs. Generators에 대한 자세한 설명보기


4

내가 이해하는 데 사용되는이 테스트 프로그램을 시도 await/ async약속합니다.

프로그램 # 1 : 약속없이 순서대로 실행되지 않음

function functionA() {
    console.log('functionA called');
    setTimeout(function() {
        console.log('functionA timeout called');
        return 10;
    }, 15000);

}

function functionB(valueA) {
    console.log('functionB called');
    setTimeout(function() {
        console.log('functionB timeout called = ' + valueA);
        return 20 + valueA;
    }, 10000);
}

function functionC(valueA, valueB) {

    console.log('functionC called');
    setTimeout(function() {
        console.log('functionC timeout called = ' + valueA);
        return valueA + valueB;
    }, 10000);

}

async function executeAsyncTask() {
    const valueA = await functionA();
    const valueB = await functionB(valueA);
    return functionC(valueA, valueB);
}
console.log('program started');
executeAsyncTask().then(function(response) {
    console.log('response called = ' + response);
});
console.log('program ended');

프로그램 # 2 : 약속

function functionA() {
    return new Promise((resolve, reject) => {
        console.log('functionA called');
        setTimeout(function() {
            console.log('functionA timeout called');
            // return 10;
            return resolve(10);
        }, 15000);
    });   
}

function functionB(valueA) {
    return new Promise((resolve, reject) => {
        console.log('functionB called');
        setTimeout(function() {
            console.log('functionB timeout called = ' + valueA);
            return resolve(20 + valueA);
        }, 10000);

    });
}

function functionC(valueA, valueB) {
    return new Promise((resolve, reject) => {
        console.log('functionC called');
        setTimeout(function() {
            console.log('functionC timeout called = ' + valueA);
            return resolve(valueA + valueB);
        }, 10000);

    });
}

async function executeAsyncTask() {
    const valueA = await functionA();
    const valueB = await functionB(valueA);
    return functionC(valueA, valueB);
}
console.log('program started');
executeAsyncTask().then(function(response) {
    console.log('response called = ' + response);
});
console.log('program ended');

0

여러면에서 생성기는 async / await의 상위 집합입니다. 현재 async / await는 가장 인기있는 async / await 유사 생성기 기반 lib 인 co 보다 더 깨끗한 스택 추적을 제공 합니다. 생성기를 사용하여 고유 한 async / await를 구현 yield하고 비약 속에 대한 내장 지원 또는 RxJS Observable에 빌드하는 것과 같은 새로운 기능을 추가 할 수 있습니다.

즉, 제너레이터는 더 많은 유연성을 제공하며 제너레이터 기반 라이브러리는 일반적으로 더 많은 기능을 제공합니다. 그러나 async / await는 언어의 핵심 부분이며 표준화되어 있으며 변경되지 않으며 사용하는 데 라이브러리가 필요하지 않습니다. async / await 및 generators의 차이점에 대한 자세한 내용 이 포함 된 블로그 게시물 이 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.