はじめに
Promise
は非同期処理の最終的な完了もしくは失敗を表すオブジェクトです。
Promise はコールバックを関数に渡すかわりに、関数が返したオブジェクトに対してコールバックを登録するようにする、というものです。
以下のコードは createAudioFileAsync()
を使用したもの
function successCallback(result) {
console.log("Audio file ready at URL: " + result);
}
function failureCallback(error) {
console.log("Error generating audio file: " + error);
}
createAudioFileAsync(audioSettings, successCallback, failureCallback);
最近では関数は Promise を返し、代わりにその Promise にコールバックを登録することができます。
createAudioFileAsync(audioSettings).then(successCallback, failureCallback);
const promise = createAudioFileAsync(audioSettings);
promise.then(successCallback, failureCallback);
メリット
- 現在の JavaScript イベントループの実行完了より前には、コールバックが決して呼び出されない。
- 非同期処理が完了もしくは失敗した後に
then()
により登録されたコールバックでも呼び出される。 -
then()
を何回も呼び出して複数のコールバックを追加してもよく、それぞれのコールバックは追加順に独立して実行される。
Promise チェーン
複数の非同期処理を順番に実行し、前の処理が完了してからその結果を次の処理で使うことができます。
then()
関数は元の Promise とは別の新しい Promise を返します。
const promise = doSomething();
const promise2 = promise.then(successCallback, failureCallback);
または以下
const promise2 = doSomething().then(successCallback, failureCallback);
2 つ目の Promise は doSomething()
の完了を表すだけではなく、渡した successCallback
もしくは failureCallback
の完了も表す。
promise2
に追加されたコールバックはいずれも Promise のキューにおいて、successCallback
または failureCallback
が返す Promise の後ろに追加されます。
基本的に、それぞれの Promise はチェーン(連鎖)上の各非同期処理の完了を表します。
Promiseを使わないと、複数の非同期処理を順番に実行するには、以下のようにする必要があった。
// Promiseを使わないと分かりにくい
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
モダンな関数を使えば、その代わりに戻り値の Promise にコールバックを付加して Promise チェーンとして記述できます。
// 一般的なニーズとしては、複数の非同期処理を順番に実行し
// 「前の処理が完了してからその結果を次の処理で使う」
// というものがあります。これは Promise チェーンを作成することで行えます。
// SuccessCallBack(resulut) = function(result){return doSomethingElse(result);}
// Promise = doSomething
// doSomething() の結果が result
// Promise = doSomething().then(function(result) {return doSomethingElse(result);})
doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
then
関数の引数はオプション(必須ではない)です。
また、catch(failureCallback)
は then(null, failureCallback)
の短縮形です。
アロー関数
//「前の処理が完了してからその結果を次の処理で使う」
//「doSomething()の結果」=「result」
//「doSomethingElse(result)の結果」=「newResult」
//「doSomethingThird(newResult)の結果」=「finalResult」
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
(アロー関数を使った () => x
は () => { return x; }
の短縮形)
catch後のチェーン
catch
の後にチェーンするのも可能です。
new Promise((resolve, reject) => {
console.log('Initial');
resolve();
})
.then(() => {
throw new Error('Something failed');
console.log('Do this');
})
.catch(() => {
console.log('Do that');
})
.then(() => {
console.log('Do this whatever happened before');
});
これは下記のテキストを出力します。
Initial
Do that
Do this whatever happened before
参考