[解決済み] 非同期関数内でのawaitの使用について
質問
私のコードのどこかにsetIntervalによって実行される非同期関数があります。この関数は、定期的にいくつかのキャッシュを更新します。
私はまた、値を取得する必要がある別の同期関数を持っています - できればキャッシュから、しかしそれがキャッシュミスであれば、データの起源から (IO 操作を同期方式で行うことは賢明でないと思いますが、この場合、これが必要であると仮定します)。
私の問題は、同期関数が非同期関数からの値を待つことができるようにしたいのですが、それは
await
キーワードの内部で、非
async
関数の中で使うことができます。
function syncFunc(key) {
if (!(key in cache)) {
await updateCacheForKey([key]);
}
}
async function updateCacheForKey(keys) {
// updates cache for given keys
...
}
の中のロジックを取り出すことで、簡単に回避することができます。
updateCacheForKey
の中のロジックを新しい同期関数に抽出し、この新しい関数を既存の両方の関数から呼び出すことで、簡単に回避できます。
私の疑問は、なぜこの使用例を最初に絶対に阻止するのかということです。私の唯一の推測は、ほとんどの場合、同期関数から非同期関数を待機することは間違っているので、quot;idiot-proofing" と関係があるということです。しかし、それは時に有効なユースケースを持っていると思うのは間違いでしょうか?
(私は、これはC#でも
Task.Wait
を使えば可能だと思います。)
どのように解決するのですか?
<ブロッククオート私の問題は、同期関数が非同期関数からの値を待つことができるようにしたいのですが...。
できないんです、なぜなら。
-
JavaScript はスレッドによって処理されるジョブキューに基づいて動作し、ジョブには実行から完了までのセマンティクスがあります。
-
JavaScript は実際には非同期関数を持っていません。
async
関数でさえも、蓋を開けてみれば、プロミスを返す同期関数です。 を返す同期関数です (詳細は後述します)。
ジョブキュー (イベントループ) は概念的には非常にシンプルです。何かを行う必要があるとき (スクリプトの初期実行、イベント ハンドラのコールバックなど)、その作業はジョブ キューに入れられます。そのジョブキューを管理するスレッドは、次の保留中のジョブをピックアップし、それを完了するまで実行し、そして次のジョブのために戻ってくるのです。 (もちろんもっと複雑ですが、今回の目的ではこれで十分です)。 つまり、関数が呼び出されるときは、ジョブの処理の一部として呼び出され、次のジョブが実行される前に必ずジョブが完了するように処理されます。
完了まで実行するということは、ジョブが関数を呼び出した場合、その関数はジョブが完了する前に戻らなければならないことを意味します。スレッドが他のことをするために走り去る間、ジョブが途中で中断されることはありません。このため、コード を劇的に 他のことが起こっている間にジョブが途中で中断される場合よりも、正しく記述し、推論することがよりシンプルになります。 (繰り返しになりますが、これよりもっと複雑ですが、ここでの目的にはこれで十分です)。
ここまではいいとして。非同期関数を本当に持っていないとはどういうことだ!?
私たちは同期関数と非同期関数について話し、さらには
async
キーワードがありますが、関数の呼び出しは常に
同期
です。また
async
関数は
同期的に
は、関数のロジックが満たすか拒否するかの約束を返します。
後に
というプロミスを返し、環境が後で呼び出すコールバックをキューに入れます。
と仮定しましょう。
updateCacheForKey
はこのように見えるとします。
async function updateCacheForKey(key) {
const value = await fetch(/*...*/);
cache[key] = value;
return value;
}
それは何かというと 本当に をやっている、布団の中で (非常に大雑把に、文字通りの意味ではなく) これです。
function updateCacheForKey(key) {
return fetch(/*...*/).then(result => {
const value = result;
cache[key] = value;
return value;
});
}
(これについては、最近の私の本の第9章に詳しく書いてあります。 JavaScript。新しいおもちゃ .)
ブラウザにデータの取得処理を開始するよう依頼し、コールバックを登録します(via.
then
を通して)コールバックを登録し、データが戻ってきたときにブラウザが呼び出すようにします。
そして終了します。
からプロミスを返します。
then
. データはまだ取得されていませんが
updateCacheForKey
は終了しています。それは返されました。それは同期的に仕事をしたのです。
後に
フェッチが完了すると、ブラウザはそのプロミス コールバックを呼び出すジョブをキューに入れます。そのジョブがキューからピックアップされると、コールバックが呼び出され、その戻り値がプロミスを解決するために使われます
then
が返されます。
私の疑問は、そもそもなぜこのユースケースを絶対に阻止するのか、ということです。
どのようなものか見てみましょう。
-
スレッドはある仕事をピックアップし、その仕事には
syncFunc
を呼び出し、それがupdateCacheForKey
.updateCacheForKey
はブラウザにリソースを取得するよう依頼し、そのプロミスを返します。この非同期の魔法によってawait
のマジックによって、私たちは同期的にそのプロミスが解決されるのを待ち、ジョブを保持します。 -
ある時点で、ブラウザのネットワークコードはリソースの取得を終了し、私たちが登録したプロミスコールバックを呼び出すためにジョブをキューに入れます。
updateCacheForKey
. -
何も起きない、もう二度と。)
...なぜなら、ジョブには run-to-completion セマンティクスがあり、スレッドは前のジョブを完了するまで次のジョブをピックアップすることが許可されていないからです。を呼び出したジョブを中断することは許されません。
syncFunc
を呼び出したジョブを中断して、その約束を解決するジョブを処理することはできません。
これは恣意的に見えますが、繰り返しになりますが、その理由は、正しいコードを書くことと、コードが何をしているかについての推論が劇的に容易になるからです。
しかし、quot;synchronous"関数がquot;asynchronous"関数の完了を待つことができないことを意味しています。
そこには ロット のような、細部の手のひら返しがあります。細かいことを知りたいなら、仕様書を掘り下げるといい。
- ジョブおよびジョブキュー
- 実行コンテキスト
- 領域 そして エージェント
関連
-
[解決済み] let "と "var "の使い分けは?
-
[解決済み] ループ内のJavaScriptクロージャ - シンプルな実用例
-
[解決済み] jQueryの「exists」関数はありますか?
-
[解決済み] JavaScriptでJSONをきれいに印刷する
-
[解決済み] forEachループでasync/awaitを使用する
-
[解決済み] async」と「await」の使い方とタイミング
-
[解決済み] 非同期アロー関数のシンタックス
-
[解決済み] async/await関数を並列に呼び出す
-
[解決済み] 非同期関数+await+setTimeoutの組合せ
-
[解決済み] ジェスト あるクラスの特定のメソッドをモックする方法
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] トップレベルでasync/awaitを使用するにはどうすればよいですか?
-
[解決済み] bootstrap のポップオーバーがすべての要素の上に表示されない
-
[解決済み] アサインの左側にJavascriptのオブジェクトブラケット表記({ ナビゲーション } =)があります。
-
[解決済み] 兄弟ノードを選択する方法はありますか?
-
[解決済み] JavaScriptを使用してHTML要素に属性を追加/更新するには?
-
[解決済み] react-routerのハッシュフラグメントからクエリパラメータを取得する
-
[解決済み] Reactメモを使うべきではない場合とは?
-
[解決済み] $.ajax実行中にローディングイメージを表示する
-
[解決済み] イテレータでmap()を使用する
-
[解決済み] jQueryを使用して、すべてのクリックイベントハンドラを削除するにはどうすればよいですか?