JavaScript で関数の終了を待つ
-
JavaScript の
Sync
とAsync
関数 -
JavaScript で関数の終了を待つには
callback
を使用する -
JavaScript で関数の終了を待つには
promises
を使用する -
実行を続ける前に関数の終了を待つには、
async/await
を使用する
このチュートリアルでは、JavaScript の Callbacks
、Promises
、Async/await
を紹介し、実行を継続する前に非同期関数が終了するのを待つ方法を示します。
Promises
と Async/await
が何であるかを理解するためには、まず JavaScript の Sync
と Async
関数が何であるかを理解する必要があります。
JavaScript の Sync
と Async
関数
同期プログラミングでは、1つのコマンドを一度に実行します。ロングランアクションを実行する関数を呼び出すと、それが終了するまでプログラムを停止します。
JavaScript は伝統的にマルチコアでもシングルスレッドです。メインスレッドと呼ばれる単一のスレッドでのみタスクを実行させることができます。
このような同期動作はマルチスレッドの制限要因ですが、同時実行の問題を気にすることなくコードを書くことができます。
非同期プログラミングでは、ロングランアクションはメインスレッド以外の別のスレッドで実行されるため、メインの実行がブロックされることはありません。長時間実行している関数が終了すると、メインプログラムに通知され、その結果にアクセスすることができます。
JavaScript のほとんどの I/O プリミティブはノンブロッキングです。ネットワークリクエストやファイルシステムの操作は、すべてノンブロッキング操作です。JavaScript では、ブロックされることは例外です。
JavaScript は非同期プログラミング技術に基づいているので、callbacks
、promises
、async/await
のような複数のアプローチがあり、関数を順番に実行できるようになっています。これにより、あるコードブロックや関数が他の特定の関数が終了する前に実行されることはありません。
上の図は、2つの関数の非同期実行と同期実行の違いを明確に示しています。
JavaScript で関数の終了を待つには callback
を使用する
同期文があれば、それらの文をお互いの後に実行するのは簡単です。
function one() {
console.log('I am function One');
}
function Two() {
console.log('I am function Two');
}
one();
Two();
出力:
I am function One
I am function Two
例えば、functionOne()
と functionTwo()
の 2つの関数を実行し、functionTwo()
の中で非同期文を実行した後に functionOne()
を実行するとします。
function functionOne(_callback) {
// do some asynchronus work
_callback();
}
function functionTwo() {
// do some asynchronus work
functionOne(() => {
console.log('I am a callback');
});
}
functionTwo();
上記のコードを実行すると、コンソールに最後に表示されるのは I am a callback
です。callback
の有名な例としては、setTimeout
関数とタイミングアウト後に実行するハンドラ関数があります。
function testCallBack() {
console.log('log before use setTimeout function');
setTimeout(() => {
console.log('inside timeout');
}, 5000);
console.log('log after use setTimeout function');
}
testCallBack();
出力:
log before use setTimeout function
log after use setTimeout function
inside timeout
JavaScript で関数の終了を待つには promises
を使用する
promise
は非同期操作の最終的な履行や失敗を表すオブジェクトです。私たちは、promise
に 1つ以上の then
文で処理の実行コールバックを付け、エラーハンドラのコールバックを catch
で呼び出すことができるようにします。
doFirstThing()
.then(result => doSecondThing(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
console.log('The final result thing' + finalResult);
})
.catch(failureCallbackfunction);
}
上の例のように then
文と catch
文を連鎖させることができるのはプロミスの利点の一つです。上の例のように then
文と catch
文を連鎖させることができるのは、プロミスの利点の一つです。then
の引数はオプションですが、結果を返さなければならない場合には必須です。
エラーが発生した場合、ブラウザはチェーンの最後に catch
を探して実行します。有名な try-catch
と非常によく似ています。
次の例は promises
チェーンを理解するのに役立ち、実行を続ける前に非同期動作を持つ関数の実行が終わるのを待つ方法を示します。
var resolvedFlag = true;
let mypromise = function functionOne(testInput) {
console.log('Entered function');
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Inside the promise');
if (resolvedFlag == true) {
resolve('Resolved');
} else {
reject('Rejected')
}
}, 2000);
});
};
mypromise()
.then((res) => {console.log(`The function recieved with value ${res}`)})
.catch((error) => {
console.log(`Handling error as we received ${error}`);
});
出力:
Entered function
Inside the promise
The function received with value Resolved
プロミスの作成は、新しい Promise()
を返すのと同じくらい簡単です。コンストラクタ Promise()
は関数を引数として受け取り、その関数には resolve
と reject
の 2つのパラメータを指定します。
イベントが成功して結果を返す必要がある場合は、resolve()
関数を用います。しかし、エラーが発生して処理する必要がある場合は、reject()
を用いてエラーを catch
に送る。
フラグ resolvedFlag = true
を設定して catch
でのエラー処理をシミュレートします。resolvedFlag
が false
に設定されている場合、関数 reject()
が呼び出され、エラーは catch
ブロックで処理されます。
出力:
Entered function
Inside the promise
Handling error as we received Rejected
実行を続ける前に関数の終了を待つには、async/await
を使用する
JavaScript の非同期環境で実行を継続する前に関数の実行を待つ別の方法として、async/wait
を利用する方法があります。
async
関数は async
キーワードで宣言された関数ですが、async
関数の内部では await
キーワードのみが許可されており、プロミスベースの非同期処理が実行されるか拒否されるまで非同期関数の内部での進行を中断するために利用されます。
async
と await
キーワードは、よりクリーンなスタイルでプロミスベースの非同期動作を可能にします。
それでは、async/await
がどのように動作するかを理解しましょう。待っている関数は、それを呼び出す前に await キーワードを使って実行されるのを待つために Promise クラスのインスタンスを返さなければなりません。上述したように、await
文を含む関数は async
文と一緒に宣言しなければなりません。
以下の例は、実行を継続する前にそのプロミスベースの関数が終了するのを待つ方法を示しています。
function testAsync() {
return new Promise((resolve, reject) => {
// here our function should be implemented
setTimeout(() => {
console.log('Hello from inside the testAsync function');
resolve();
;
}, 5000);
});
}
async function callerFun() {
console.log('Caller');
await testAsync();
console.log('After waiting');
}
callerFun();
出力:
Caller
Hello from inside the testAsync function
After waiting