私はちょうどこの素晴らしい記事を読んでいた-
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/
さて、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/
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
を使用して、生成された値を約束としてアンラップし、解決された値を返して、関数が最終値を返すまで繰り返すこの機構です。
await
キーワードとyield
キーワードの違いは何ですか?
await
キーワードはasync function
sでのみ使用され、yield
キーワードはジェネレーターfunction*
sでのみ使用されます。そして、これらも明らかに異なります。1つはpromiseを返し、もう1つはジェネレータを返します。
await
は常に何かを約束に変えますが、yield
はそのような保証を行いませんか?
はい、await
は待機値でPromise.resolve
を呼び出します。
yield
は、ジェネレーターの外部の値のみを生成します。
ジェネレーターの99%の時間で非同期/待機を使用します。なぜですか?
Async/Awaitは、promiseチェーンの最も一般的なワークフローを直接置き換えて、コードを同期のように宣言できるようにし、劇的に簡素化します。
ジェネレーターは、相互に依存し、最終的に「完了」状態になる一連の非同期操作を呼び出すユースケースを抽象化します。最も簡単な例は、最終的に最後のセットを返す結果をページングすることですが、すぐに連続してではなく、必要に応じてページを呼び出すだけです。
Async/Awaitは、実際にはジェネレーターの上に構築された抽象化であり、Promiseを簡単に処理できるようにします。
約束との待機/非同期を理解するために使用したこのテストプログラムを試してください
プログラム#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');
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');
多くの点で、ジェネレーターはasync/awaitのスーパーセットです。現在、async/awaitのスタックトレースは、最も一般的なasync/awaitのようなジェネレータベースのライブラリである co よりもきれいです。ジェネレーターを使用して独自のフレーバーのasync/awaitを実装し、非プロミスでのyield
の組み込みサポートやRxJSオブザーバブルでのビルドのような新しい機能を追加できます。
要するに、ジェネレーターは柔軟性を高め、ジェネレーターベースのライブラリは一般的に多くの機能を備えています。しかし、async/awaitは言語の中核部分であり、標準化されており、ユーザーの下で変更されることはありません。また、使用するためにライブラリは必要ありません。 ブログ投稿 があり、async/awaitとジェネレーターの違いについて詳しく説明しています。