web-dev-qa-db-ja.com

ジェネレーターでのasync / awaitとES6 yieldの違い

私はちょうどこの素晴らしい記事を読んでいた-

https://www.promisejs.org/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と似ていることを確認できます。この記事では、 https://jakearchibald.com/2014/es7-async-functions/

62
Alexander Mills

さて、async/awaitとジェネレーターの間には非常に密接な関係があることがわかりました。そして、私はasync/awaitは常にジェネレーター上に構築されると信じています。 Babelがasync/awaitをトランスパイルする方法を見ると:

バベルはこれを取る:

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/

32
Alexander Mills

yieldは、awaitの構成要素と見なすことができます。 yieldは、指定された値を取り、呼び出し元に渡します。呼び出し元は、その値(1)を使用して、必要な処理を実行できます。後で、呼び出し元は、yield式の結果(2)、またはyield式によってスローされるように見えるエラー(3)になるジェネレーターに値を返すことがあります(generator.next()を介して)。

async-awaitは、yieldを使用すると見なすことができます。 (1)で、呼び出し元(つまり、async-awaitドライバー-投稿した関数に類似)は、new Promise(r => r(value)と同様のアルゴリズムを使用して値をpromiseにラップします(注、notPromise.resolve、しかしそれは大したことではありません)。次に、約束が解決するのを待ちます。満たす場合、(2)で満たされた値を返します。拒否した場合、(3)でエラーとして拒否理由をスローします。

したがって、async-awaitのユーティリティは、yieldを使用して、生成された値を約束としてアンラップし、解決された値を返して、関数が最終値を返すまで繰り返すこの機構です。

37
Arnavion

awaitキーワードとyieldキーワードの違いは何ですか?

awaitキーワードはasync functionsでのみ使用され、yieldキーワードはジェネレーターfunction*sでのみ使用されます。そして、これらも明らかに異なります。1つはpromiseを返し、もう1つはジェネレータを返します。

awaitは常に何かを約束に変えますが、yieldはそのような保証を行いませんか?

はい、awaitは待機値でPromise.resolveを呼び出します。

yieldは、ジェネレーターの外部の値のみを生成します。

25
Bergi

tldr;

ジェネレーターの99%の時間で非同期/待機を使用します。なぜですか?

  1. Async/Awaitは、promiseチェーンの最も一般的なワークフローを直接置き換えて、コードを同期のように宣言できるようにし、劇的に簡素化します。

  2. ジェネレーターは、相互に依存し、最終的に「完了」状態になる一連の非同期操作を呼び出すユースケースを抽象化します。最も簡単な例は、最終的に最後のセットを返す結果をページングすることですが、すぐに連続してではなく、必要に応じてページを呼び出すだけです。

  3. Async/Awaitは、実際にはジェネレーターの上に構築された抽象化であり、Promiseを簡単に処理できるようにします。

非同期/待機対ジェネレータの詳細な説明を参照

5
Jason Sebring

約束との待機/非同期を理解するために使用したこのテストプログラムを試してください

プログラム#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');
3
Kamal Kumar

多くの点で、ジェネレーターはasync/awaitのスーパーセットです。現在、async/awaitのスタックトレースは、最も一般的なasync/awaitのようなジェネレータベースのライブラリである co よりもきれいです。ジェネレーターを使用して独自のフレーバーのasync/awaitを実装し、非プロミスでのyieldの組み込みサポートやRxJSオブザーバブルでのビルドのような新しい機能を追加できます。

要するに、ジェネレーターは柔軟性を高め、ジェネレーターベースのライブラリは一般的に多くの機能を備えています。しかし、async/awaitは言語の中核部分であり、標準化されており、ユーザーの下で変更されることはありません。また、使用するためにライブラリは必要ありません。 ブログ投稿 があり、async/awaitとジェネレーターの違いについて詳しく説明しています。

0
vkarpov15