なぜJavaScriptで最上階のawaitを使う必要がありますか?
10176 ワード
最上階のawait(
top-level await
)は、近年の提案に関わる新たな特性である.この特性は、ESモジュールを外部にasync
関数として表現することができ、ESモジュールがawait
データに行き、他のデータを導入するモジュールをブロックすることができる.データが確定して準備されている場合のみ、データを導入するモジュールがコードを実行できます.この特性に関する提案はまだstage 3段階にありますので、直接生産環境では使用できません.しかし、近い未来に発売される予定なので、あらかじめ調べておくといいです.
霧のように聞こえても大丈夫です.続けて読んでください.あなたと一緒にこの新しい特性を解決します.
以前の書き方はどこでしたか?
トップレベルのawaitを導入する前に、
async
関数の外でawait
キーワードを使用しようとすると、文法的エラーが発生します.この問題を避けるために、開発者は通常すぐに関数式(IIIFE)を実行します.await Promise.resolve(console.log('❤️'));
//
(async () => {
await Promise.resolve(console.log('❤️'));
//❤️
})();
しかし、これは氷山の一角にすぎない.ES 6を使ってモジュール化すると、導入してエクスポートするシーンがよくあります.この例を見てください.
//------ library.js ------
export const sqrt = Math.sqrt;
export function square(x) {
return x * x;
}
export function diagonal(x, y) {
return sqrt(square(x) + square(y));
}
//------ middleware.js ------
import { square, diagonal } from './library.js';
console.log('From Middleware');
let squareOutput;
let diagonalOutput;
// IIFE
(async () => {
await delay(1000);
squareOutput = square(13);
diagonalOutput = diagonal(12, 5);
})();
function delay(delayInms) {
return new Promise(resolve => {
setTimeout(() => {
resolve(console.log('❤️'));
}, delayInms);
});
}
export {squareOutput,diagonalOutput};
この例では、library.js
とmiddleware.js
の間で変数の導入導出を行います.よく読むと、
delay
関数があります.Promiseは時間終了後、resoliveに返されます.これは非同期的な動作であるため(実際のビジネスシーンでは、ここではfetch呼び出しまたは何らかの非同期タスクであるかもしれない)、async
IIIIIIFEでawait
を使用して、その実行結果を待つ.一旦promiseがresoliveされると、library.js
から導入された関数を実行し、計算された結果を二つの変数に割り当てます.これはpromiseがresoliveされる前に、両方の変数がundefined
であることを意味する.コードの一番後ろに、計算された二つの変数を別のモジュールで使用するために導出します.
このモジュールは以下の二つの変数を導入して使用します.
//------ main.js ------
import { squareOutput, diagonalOutput } from './middleware.js';
console.log(squareOutput); // undefined
console.log(diagonalOutput); // undefined
console.log('From Main');
setTimeout(() => console.log(squareOutput), 2000);
//169
setTimeout(() => console.log(diagonalOutput), 2000);
//13
上のコードを実行すると、前の2回の印刷で得られたのは全部undefined
で、後の2回の印刷で得られたのは169と13です.どうしてですか?これは、
async
関数が実行される前にmain.js
がmiddleware.js
によって導出された変数にアクセスしたからである.覚えていますか?私たちの前にはもう一つのプロミスがあります.レレスを待っています.この問題を解決するためには、変数にアクセスする準備ができたら変数を導入するようにモジュールに通知する方法が必要です.
ソリューション
上記の問題に対して、広く使われている二つの解決策があります.
1.Promise表示をエクスポートし初期化する
IIIFを導き出すことができます.結果にアクセスできるタイミングを決定します.
async
キーワードは、1つの方法を非同期化し、対応するプロミスを返しますを有する.したがって、以下のコードの中で、async
IIIIIIIFEはプロミセに戻ります.//------ middleware.js ------
import { square, diagonal } from './library.js';
console.log('From Middleware');
let squareOutput;
let diagonalOutput;
//
export default (async () => {
await delay(1000);
squareOutput = square(13);
diagonalOutput = diagonal(12, 5);
})();
function delay(delayInms) {
return new Promise(resolve => {
setTimeout(() => {
resolve(console.log('❤️'));
}, delayInms);
});
}
export {squareOutput,diagonalOutput};
main.js
でエクスポート結果にアクセスすると、async
IIIIIIIIFREがresoliveされてから変数にアクセスすることができます.//------ main.js ------
import promise, { squareOutput, diagonalOutput } from './middleware.js';
promise.then(()=>{
console.log(squareOutput); // 169
console.log(diagonalOutput); // 13
console.log('From Main');
setTimeout(() => console.log(squareOutput), 2000);// 169
setTimeout(() => console.log(diagonalOutput), 2000);// 13
})
この案は有効であるが、新しい問題も導入された.main.js
内の変数squareOutput
およびdiagonalOutput
に依存している場合、同じようなIIIF promiseを再度作成して導出し、他のモジュールが変数に正しくアクセスできるようにする必要がある.2.導出した変数でresove IIIIF promiseに行く
この方式では、以前のように変数を単独で導出するのではなく、
async
IIIIFEの戻り値として変数を返します.そうすれば、main.js
はpromiseがresoliveされるのを簡単に待つだけで、その後直接変数を取得すればいいです.//------ middleware.js ------
import { square, diagonal } from './library.js';
console.log('From Middleware');
let squareOutput;
let diagonalOutput;
export default (async () => {
await delay(1000);
squareOutput = square(13);
diagonalOutput = diagonal(12, 5);
return {squareOutput,diagonalOutput};
})();
function delay(delayInms) {
return new Promise(resolve => {
setTimeout(() => {
resolve(console.log('❤️'));
}, delayInms);
});
}
//------ main.js ------
import promise from './middleware.js';
promise.then(({squareOutput,diagonalOutput})=>{
console.log(squareOutput); // 169
console.log(diagonalOutput); // 13
console.log('From Main');
setTimeout(() => console.log(squareOutput), 2000);// 169
setTimeout(() => console.log(diagonalOutput), 2000);// 13
})
しかし、この案にはその自身の複雑さがある.提案によると、このようなモードの悪影響は、動的モードを使用するために関連するデータを大規模に再構成することを要求することである.一方、モジュールの大部分のコンテンツを
.then()
のコールバック関数において動的に導入することができる.静的分析、試験可能性、工学および他の観点から、ES 2015のモジュール化に比べて明らかである.後戻りするトップレベルのAwaitはどうやって上記の問題を解決しますか?
トップ
await
は、モジュールシステムにpromise間の協調関係を処理させ、こちらの作業を非常に簡単にすることができます.//------ middleware.js ------
import { square, diagonal } from './library.js';
console.log('From Middleware');
let squareOutput;
let diagonalOutput;
// await
await delay(1000);
squareOutput = square(13);
diagonalOutput = diagonal(12, 5);
function delay(delayInms) {
return new Promise(resolve => {
setTimeout(() => {
resolve(console.log('❤️'));
}, delayInms);
});
}
export {squareOutput,diagonalOutput};
//------ main.js ------
import { squareOutput, diagonalOutput } from './middleware.js';
console.log(squareOutput); // 169
console.log(diagonalOutput); // 13
console.log('From Main');
setTimeout(() => console.log(squareOutput), 2000);// 169
setTimeout(() => console.log(diagonalOutput), 2000); // 13
middleware.js
の中のawait
promiseがresoliveされる前に、main.js
の中のいずれかの文が実行されません.前に述べた解決策と比べて、この方法はより簡潔でなければなりません.注意
トップレベルのawaitはESモジュールだけで有効です.また、モジュール間の依存関係を明示してこそ、トップレベルのawaitが期待通りに有効になることができます.提案倉庫のコードはこの問題をよく説明しました.
// x.mjs
console.log("X1");
await new Promise(r => setTimeout(r, 1000));
console.log("X2");
// y.mjs
console.log("Y");
// z.mjs
import "./x.mjs";
import "./y.mjs";
//X1
//Y
//X2
このコードの印刷の順序は予想されるX1,X2,Y
ではない.x
とy
は独立したモジュールであり、互いに依存関係がないからである.文書クイズを読むことをお勧めします.この最上階のawaitという新しい特性をより全面的に知ることができます.
仮採用
V 8
文書によって言われたように、トップ
await
の特性を使ってみてもいいです.私が使っているのはV 8の方法です.あなたのコンピュータ上のChromeブラウザの設置場所を見つけて、ブラウザを閉じるすべてのプロセスを確保して、コマンドラインを開いて次のコマンドを実行します.
chrome.exe --js-flags="--harmony-top-level-await"
このようにChromeが再オープンすると最上階のawait特性に対するサポートがオープンします.もちろん、Node環境テストもできます.このガイドを読んで、より多くの詳細を取得します.
ESモジュール
script
タグにこの属性を宣言することを保証する:type="module"
通常のスクリプトとは異なり、モジュール化後のスクリプトはCORSポリシーの影響を受けますので、サーバーを介してファイルを開く必要があります.アプリケーションシーン
提案で述べた関連例は以下の通りです.
動的依存経路
const strings = await import(`/i18n/${navigator.language}`);
モジュールの運転時の値を使って依存関係を計算することができます.これは生産・開発環境の区分や国際的な仕事などに有効です.リソース初期化
const connection = await dbConnector();
これはモジュールをある種の資源と見なし、モジュールが存在しない時にエラーを出すことができます.エラーは下記に紹介されたバックアッププログラムで処理されます.依存予備案
以下の例は、トップ
await
を用いてバックアップスキームを伴う依存性をロードする方法を示しており、CDN AがjQueryを導入できない場合、CDN Bから導入する試みがある.let jQuery;
try {
jQuery = await import('https://cdn-a.example.com/jQuery');
} catch {
jQuery = await import('https://cdn-b.example.com/jQuery');
}
攻撃するトップ
await
の特性に対して、Rich Harrisは多くのバッシング的な問題を提案している.await
は、コードの実行をブロックします.await
は、リソースの取得をブロックします.ステージ3の提案は、これらの問題を直接解決しました.
await
は、モジュール図の実行段階で機能し、このとき、すべてのリソースがすでに取得され、リンクされているので、リソースがブロックされるリスクは存在しない.await
はES 6モジュールに限定されており、それ自体は通常のスクリプトまたはCommonJSモジュール読者の皆さんには、提案したFAQを読んで、この新しい特性に対する理解を深めることを強く勧めます.
ここを見て、このクールな新しい特性についてある程度知っていると思います.もうこれを使ってみたいですか?コメントエリアで一緒に交流しましょう.