JavaScript(特にNode.js)で非同期処理は便利だけど、async/await、Promiseについて使い方がよくわからないという方に向けてご紹介します。
asyncの使い方
async
とは非同期処理の関数宣言のことです。
async function example(arg){
conset variable1 = 1;
conset variable2 = 2;
conset variable3 = 3;
}
宣言された関数の中の処理は全て非同期になります。通常のコード(同期)では、上から順番に処理されるため、variable1に1が代入された後にvariable2に2を代入する処理が行われます。しかし、非同期処理では、variable1に1の代入が完了する前に、variable2に2を代入する処理を走らせます。
非同期処理では、処理の完了を待たずに次の処理が実行されることが特徴です。
awaitの使い方
await
とはasync
関数の中でPromiseの結果が返されるまで待機する宣言のことです。
async function example(arg){
const url = "https://xxx";
const res = await fetch(url);
console.log(res)
}
上記の例では、fetch処理が完了するまで待機し、fetchの結果をres変数に代入します。その後、async関数の続き(console.log)の実行を再開します。
await
はasync関数の中でしか宣言できないので注意しましょう。
Promiseの使い方
Promise
オブジェクトは非同期処理の最終的な完了処理 (もしくは失敗) およびその結果の値を返します。
// Promiseを変数の処理として扱う場合
let width = 3;
let height = 5;
const rectangle_area = new Promise( (resolve, reject) => {
try{
resolve(width * height);
}catch(error){
reject(error)
}
});
------
// Promiseを関数の処理として扱う場合
function rectangleArea(width, height){
return new Promise( (resolve, reject) => {
try{
resolve(width * height);
}catch(error){
reject(error)
}
});
}
async () => {
const rectangle_area = await rectangleArea(3, 5);
}
上記の例では、横幅(width)と縦幅(height)を掛け算した結果をrectangle_area変数に代入しています。
try catch
で囲っているのは、非同期処理が正しく処理された場合と、エラーが発生した場合に切り分けるためです。(今回の例でエラーになることはないのですが、実際はAPI通信などを行うのでエラーハンドリングは必須処理です)
非同期処理が正常に動作した場合、resolveで返し、エラーが発生した場合rejectで返すという使い分けをします。
実践的な使い方 – 個別の非同期処理をまとめて行う
互いに依存関係のない個別の非同期処理を複数個まとめて行う場合を考えます。
今回の例では、四角形の面積と円の面積と三角形の面積を求め、その後全て整数になるよう四捨五入させます。
function rectangleArea(width, height){
// 四角形の面積を求める処理
return new Promise( (resolve, reject)=>{
try{
resolve(width*height);
}catch(error){
reject(error)
}
});
}
function triangleArea(width, height){
// 三角形の面積を求める処理
return new Promise( (resolve, reject)=>{
try{
resolve(width*height/2);
}catch(error){
reject(error)
}
});
}
function circleArea(radius){
// 円の面積を求める処理
return new Promise( (resolve, reject)=>{
try{
resolve(radius*radius*Math.PI);
}catch(error){
reject(error)
}
});
}
async function calculatedCircle(width, height, radius){
try{
const results = await Promise.all([
rectangleArea(width, height),
triangleArea(width, height),
circleArea(radius)
]);
const result_rounded = await Promise.all(results.map( result => Math.round(result)));
return result_rounded;
}catch(error){
console.log(error)
}
}
(async () => {
const width = 3.2;
const height = 5;
const radius = 2;
const data = await calculatedCircle(width, height, radius);
console.log(data);
})();
上記の例では、Promiseを使った面積を求める関数を定義し、calculatedCircle関数の中で一斉に面積を求める計算をしています。求めた面積をmapメソッドを使って、四捨五入しています。
Promise.all
は全ての非同期処理を同時に実行し、全ての処理が完了した後に値を返します。
これにより、一つ一つ計算するより早く処理が完了します。
実践的な使い方 – 連続した非同期処理を行う
一つの処理結果が次の処理に影響を与える場合について考えます。
今回の例では、正方形の中に円がある場合、下図の斜線部分の面積を求める問題を考えてみましょう。
function rectangleArea(length){
// 正方形の面積を求める処理
return new Promise( (resolve, reject)=>{
try{
resolve(length*length);
}catch(error){
reject(error)
}
});
}
function circleArea(radius){
// 円の面積を求める処理
return new Promise( (resolve, reject)=>{
try{
resolve(radius*radius*Math.PI);
}catch(error){
reject(error)
}
});
}
function calcuratedArea(length){
let answer;
return rectangleArea(length)
.then( rec => {
answer = rec;
return circleArea(length/2);
})
.then( cir => {
answer = (answer - cir) / 4;
return answer;
})
}
calcuratedArea(5).then( ans => {
console.log(ans);
});
ここでのポイントは、 then()
で処理を連結しているところです。
正方形の面積の計算結果をthen
の引数に渡し、return
で次の円の面積を求める処理を宣言します。
次に、円の面積の計算結果を使って、最終的に求める面積を計算しています。
return
で返す処理も非同期処理なら続けてthen()
で連結することができ、値を返せば最終的な返り値となります。
Promise.all
の場合は、互いに独立した処理だったので同時に実行できましたが、一つの処理結果を次の処理に利用する場合には、thenで連結して処理を行います。
コメント