JavaScriptでの非同期/待機関数の調査
序章
Promises を使用すると、コードの非同期を順番に処理する簡単な方法が得られます。 私たちの脳が非同期性を効率的に処理するように設計されていないことを考えると、これは大いに歓迎される追加です。 ES2017( ES8 )に新しく追加された Async / await関数は、舞台裏で非同期タスクを実行しながら、完全に同期しているように見えるコードを記述できるようにするのにさらに役立ちます。
非同期関数を使用して実現された機能は、promiseをジェネレーターと組み合わせることで再現できますが、非同期関数は、追加の定型コードなしで必要なものを提供します。
簡単な例
次の例では、最初に、次の値に解決されるPromiseを返す関数を宣言します。 🤡
2秒後。 次に、 async 関数を宣言し、メッセージをコンソールに記録する前に、解決する約束をawaitします。
function scaryClown() {
return new Promise(resolve => {
setTimeout(() => {
resolve('🤡');
}, 2000);
});
}
async function msg() {
const msg = await scaryClown();
console.log('Message:', msg);
}
msg(); // Message: 🤡 <-- after 2 seconds
await
は、プロミスが解決または拒否されるのを待つために使用される新しい演算子です。 非同期関数内でのみ使用できます。
複数のステップが含まれる場合、非同期関数の能力はより明白になります。
function who() {
return new Promise(resolve => {
setTimeout(() => {
resolve('🤡');
}, 200);
});
}
function what() {
return new Promise(resolve => {
setTimeout(() => {
resolve('lurks');
}, 300);
});
}
function where() {
return new Promise(resolve => {
setTimeout(() => {
resolve('in the shadows');
}, 500);
});
}
async function msg() {
const a = await who();
const b = await what();
const c = await where();
console.log(`${ a } ${ b } ${ c }`);
}
msg(); // 🤡 lurks in the shadows <-- after 1 second
ただし、上記の例では、各ステップが順番に実行され、追加の各ステップは、続行する前に解決または拒否する前のステップを待機していることに注意してください。 代わりに、手順を並行して実行する場合は、 Promise.all を使用して、すべての約束が実行されるのを待つことができます。
// ...
async function msg() {
const [a, b, c] = await Promise.all([who(), what(), where()]);
console.log(`${ a } ${ b } ${ c }`);
}
msg(); // 🤡 lurks in the shadows <-- after 500ms
Promise.all は、渡されたすべてのPromiseが解決されると、解決された値を含む配列を返します。
上記では、コードを簡潔にするために、いくつかの優れた配列分解も使用しています。
約束-帰る
非同期関数は常にpromiseを返すため、以下では目的の結果が得られない場合があります。
async function hello() {
return 'Hello Alligator!';
}
const b = hello();
console.log(b); // [object Promise] { ... }
返されるのは約束なので、代わりに次のようなことを行うことができます。
async function hello() {
return 'Hello Alligator!';
}
const b = hello();
b.then(x => console.log(x)); // Hello Alligator!
…またはこれだけ:
async function hello() {
return 'Hello Alligator!';
}
hello().then(x => console.log(x)); // Hello Alligator!
さまざまなフォーム
これまでの例では、非同期関数を関数宣言として見てきましたが、非同期関数式と非同期矢印関数を定義することもできます。
非同期関数式
これが最初の例の非同期関数ですが、関数式として定義されています。
const msg = async function() {
const msg = await scaryClown();
console.log('Message:', msg);
}
非同期矢印関数
これも同じ例ですが、今回は矢印関数として定義されています。
const msg = async () => {
const msg = await scaryClown();
console.log('Message:', msg);
}
エラー処理
非同期関数のもう1つの優れた点は、古き良きtry…catchステートメントを使用して、エラー処理も完全に同期的に実行されることです。 半分の時間を拒否するpromiseを使用してデモンストレーションしましょう。
function yayOrNay() {
return new Promise((resolve, reject) => {
const val = Math.round(Math.random() * 1); // 0 or 1, at random
val ? resolve('Lucky!!') : reject('Nope 😠');
});
}
async function msg() {
try {
const msg = await yayOrNay();
console.log(msg);
} catch(err) {
console.log(err);
}
}
msg(); // Lucky!!
msg(); // Lucky!!
msg(); // Lucky!!
msg(); // Nope 😠
msg(); // Lucky!!
msg(); // Nope 😠
msg(); // Nope 😠
msg(); // Nope 😠
msg(); // Nope 😠
msg(); // Lucky!!
非同期関数は常にpromiseを返すため、通常のcatchステートメントを使用する場合と同様に、未処理のエラーを処理することもできます。
async function msg() {
const msg = await yayOrNay();
console.log(msg);
}
msg().catch(x => console.log(x));
この同期エラー処理は、promiseが拒否された場合だけでなく、実際のランタイムエラーまたは構文エラーが発生した場合にも機能します。 次の例では、2回目に msg 関数を呼び出して、プロトタイプチェーンにtoUpperCaseメソッドがないnumber値を渡します。 try…catchブロックもそのエラーをキャッチします。
function caserUpper(val) {
return new Promise((resolve, reject) => {
resolve(val.toUpperCase());
});
}
async function msg(x) {
try {
const msg = await caserUpper(x);
console.log(msg);
} catch(err) {
console.log('Ohh no:', err.message);
}
}
msg('Hello'); // HELLO
msg(34); // Ohh no: val.toUpperCase is not a function
PromiseベースのAPIを使用した非同期関数
Fetch API の入門書で示したように、promiseベースのWeb APIは、非同期関数の最適な候補です。
async function fetchUsers(endpoint) {
const res = await fetch(endpoint);
let data = await res.json();
data = data.map(user => user.username);
console.log(data);
}
fetchUsers('https://jsonplaceholder.typicode.com/users');
// ["Bret", "Antonette", "Samantha", "Karianne", "Kamren", "Leopoldo_Corkery", "Elwyn.Skiles", "Maxime_Nienow", "Delphine", "Moriah.Stanton"]
ブラウザのサポート: 2020年現在、世界中の 94% ofブラウザはjavascriptで非同期/待機を処理できます注目すべき例外はIE11とOperaMiniです。
結論
Async / await関数の前は、多くの非同期イベントに依存するJavaScriptコード(例:APIを大量に呼び出すコード)は、「コールバック地獄」と呼ばれるものになってしまいます。 」-読み取りと理解が非常に困難だった一連の関数とコールバック。
Asyncとawaitを使用すると、より明確に読み取る非同期JavaScriptコードを記述できます。