アナログCPU:5108843109

ゲームと音楽とプログラミング(酒と女とロックンロールのノリで)

('ω') < イザユケエンジニャー

非同期処理を同期処理っぽく扱う

まず、以下2つの関数を用意しました。

// funcBの戻り値と"funcA"を吐き出す関数
function funcA(){
    console.log(funcB());
    console.log("funcA");
}

// 開始から5秒後に「funcB」とreturnする関数
function funcB(){
    setTimeout(function(){
        return "funcB";
    },5000);
}

期待としては、実行から5秒後に「funcB」と「funcA」が
(ほぼ)同時に表示されてほしいわけですが、まあそういうわけにはいきません。
setTimeoutは「5秒後に○○という処理をする」というタイマー的なものをセットしているだけなので。
具体的には、実行と(ほぼ)同時に

undefined
funcA

と表示されます。
その5秒後くらいにfuncBが返している値は闇に消えます。

ここまで簡単な例ならsetTimeout内で「funcB」「funcA」を表示すりゃいいじゃん、で済みますが
複雑になってくると困る場合もあったので、いろいろ調べて以下のように加筆修正してみました。

// funcBの戻り値と"funcA"を吐き出す関数
function funcA(){
    var def = funcB();
    def.done(function(){
        console.log(def.return_value);
        console.log("funcA");
    });
}

// 開始から5秒後に「funcB」とreturnする関数
function funcB(){
    var deferred = new $.Deferred();
    setTimeout(function(){
        deferred.return_value = "funcB";
        deferred.resolve();
    },5000);
    return deferred;
}

わーいらくちーん
deferredというのを使っています。
処理を監視して、「終了したら○○する」ということができます。

無事に、実行後5秒ほど経ってから

funcB
funcA

と表示されました。

ポイントは以下のような感じでしょうか。

  • funcA
    • funcBの戻り値を変数で受けて、「.done」で「funcB終了時の処理」を記述
  • funcB
    • Deferredオブジェクトを生成し、resolve()で処理終了したことを通知
    • Deferredオブジェクトのreturnは、時間のかかる処理の外でOK
    • Deferredオブジェクトには戻り値用の要素を勝手に追加できる
      • 上記の例では「return_value」が勝手に追加したやつ

これなら大元のロジックを大きく変えることなく実装できそう。

参考文献
jQueryのDeferredが便利過ぎた