Promiseの使い方/JavaScript

コールバック関数はネスト(入れ子)にする事が出来ますが、複雑になります。このような時は、Promiseを使うと便利です。

本項では、Promiseについて説明します。

コールバック関数のネスト

 関数を順番に呼び出したい時、コールバック関数をネスト出来ます。

コールバックのネスト

 以下は、例です。

【コールバック関数のネスト例】
<script>
function func1(callback) {
    alert("func1完了");
    callback();
}
function func2(callback) {
    alert("func2完了");
    callback();
}
function func3() {
    alert("func3完了");
}

func1(function() {
    func2(function() {
        func1(function() {
            func2(func3);
        });
    });
});
</script>

 上記はfunc1→func2→func1→func2→func3の順番で呼び出されますが、赤字の所が分かり辛いと思います。これに引数や変数等が加わってくると、記述していても間違いやすくなります。

 このようにコールバック関数は、簡単に次の関数を指定出来るメリットがありますが、ネストすると分かり辛いデメリットがあります。

Promise.resolve

 先ほどの例をPromiseを使って書き換えると、以下になります。

【Promise.resolveの利用例】
<script>
function func1() {
    alert("func1完了");
}
function func2() {
    alert("func2完了");
}
function func3() {
    alert("func3完了");
}

Promise.resolve()
    .then(func1)
    .then(func2)
    .then(func1)
    .then(func2)
    .then(func3);
</script>

 赤字が書き換えた部分ですが、非常に分かり易くなったと思います。Promiseがオブジェクトで、resolveやthenがメソッドです。func1等は、関数宣言せずに.then(function() {alert("func1完了");})のように無名の関数式で記述する事も出来ます。

 このように、複数の関数を呼び出す必要がある時はPromiseを使うと便利ですが、InternetExplorerではサポートされていません。

チェーン

 引数は、以下のようにして渡せます。

【Promise.resolveでの引数利用例】
<script>
function func1(x) {
    alert(x);
    return "func2完了";
}
function func2(y) {
    alert(y);
}

Promise.resolve("func1完了")
    .then(func1)
    .then(func2);
</script>

 赤字部分で「func1完了」がfunc1に渡されます。青字部分で「func2完了」がfunc2に渡されます。このように、次のthenに引数を渡す事をチェーンと呼びます。

 もし、チェーンで渡したくない場合、青字部分を削除し、.then(func2)を.then(function() {func2("func2完了");})に書き換えると引数として渡せます。

エグゼキューター関数

 Promiseは、成功と失敗で処理を分けて記述出来ます。

【Promiseの利用例】
<script>
function func1(resolve, reject){
    x = Math.ceil( Math.random() * 2);
    if(x == 1){
        resolve("成功");
    } else {
        reject("失敗");
    }
}

function func2(z){
    alert(z);
}

var promise1 = new Promise(func1);
promise1
    .then(func2)
    .catch(func2);
</script>

 赤字のresolveで引数に"成功"を渡し、thenで成功時の処理を行っています。青字のrejectで引数に"失敗"を渡し、catchで失敗時の処理を行っています。このため、ランダムなxが1であれば「成功」、それ以外は「失敗」と表示されます。

 func1はエグゼキューター関数と呼ばれ、resolve(成功)とreject(失敗)の値を返し、thenとcatchで引数として使えます。

 尚、catchの代わりにthenで.then(func2,func2)と記述しても同じです。第一パラメタが成功時、第二パラメタが失敗時の処理を示します。

非同期処理との組み合わせ

 「sleep処理」でご紹介したsetTimeoutと組み合わせて利用する場合の例は、以下の通りです。

【PromiseとsetTimeoutの組み合わせ例】
<script>
function func1(x){
    return new Promise(function(resolve,reject){
        setTimeout(function(){resolve();},x);
    });
}
function func2() {
    alert("5秒経ちました");
}

Promise.resolve(5000)
    .then(func1)
    .then(func2);
</script>

 赤字部分でプロミスの成功時だけを扱っています。func1が実行され、xが5000にセットされるため、青字部分のsetTimeoutでは5秒待つ事になります。5秒後にresolve();が実行されますが、これが成功を示します。returnがあるため、成功を示す応答があるまで待ち、次のthenでfunc2が処理されます。つまり、5秒後にfunc2が実行出来ます。

 次は、addEventListenerの例です。

【PromiseとaddEventListenerの組み合わせ例】
<div>
<image src="image/trump/11.png" alt="スペードA" id="image1">
</div>

<script>
function func1() {
    alert("クリックされました");
}
function func2() {
    alert("スペードのAです");
}

var x = document.getElementById("image1");
var promise1 = function() {Promise.resolve()
    .then(func1)
    .then(func2);
};

x.addEventListener("click", promise1, false);
</script>

 赤字部分で処理を記述し、青字部分でイベントリスナーのハンドラとして登録しています。このため、画像をクリックすると「クリックされました」「スペードのAです」の順に表示されます。thenで呼び出す関数を変える事で、異なる処理をさせる事も可能です。

まとめ

 非同期処理で複数関数を処理すると、順番が分かり辛くなりがちです。

 Promiseを使うと、.then(func1)、.then(func2)等、非同期処理の順番を分かり易く記述出来ます。又、チェーンによって、簡単に次の処理に値を渡す事が出来ます。

前のページsleep処理

  • このエントリーをはてなブックマークに追加