JavaScript
jQuery
Deferred

【超便利!】jQueryを使った並列処理のやり方

やりたいこと

処理Aと処理Bと処理Cが全部終わってから処理Dを実行したい!

例えば以下のような4つの処理があったとして、
↓↓↓

myApp.js
function execA() {
    setTimeout(function() {
        console.log('処理A');
    }, 2000);
}
function execB() {
    setTimeout(function() {
        console.log('処理B');
    }, 1000);
}
function execC() {
    setTimeout(function() {
        console.log('処理C');
    }, 4000);
}
function execD() {
    console.log('処理D');
}

これらを順番に実行していくと、
↓↓↓

myApp.js
execA();
execB();
execC();
execD();

結果は、
↓↓↓

// 処理D
// 処理B
// 処理A
// 処理C

こうなります。処理Aから処理CはsetTimeout()を使って、それぞれの処理開始をずらしているので、このような結果になっています。そこで今回のやりたいこと、「処理Aと処理Bと処理Cが全部終わってから処理Dを実行したい!」を実現していきたいと思います。

やり方

jQueryのDeferredオブジェクトを使って実現していきます。
まずは、処理Aから処理CをDeferredオブジェクトを使った以下のような形に修正します。

myApp.js
function execA() {
    var def = $.Deferred();         // 追加
    setTimeout(function() {
        console.log('処理A');
        def.resolve();              // 追加
    }, 2000);
    return def.promise();           // 追加
}
function execB() {
    var def = $.Deferred();         // 追加
    setTimeout(function() {
        console.log('処理B');
        def.resolve();              // 追加
    }, 1000);
    return def.promise();           // 追加
}
function execC() {
    var def = $.Deferred();         // 追加
    setTimeout(function() {
        console.log('処理C');
        def.resolve();              // 追加
    }, 4000);
    return def.promise();           // 追加
}

ここで各処理に加えた3つの修正点について簡単に説明していきます。
↓↓↓

  1. var def = $.Deferred();
    • ローカルスコープにDeferredオブジェクトを定義しています。このオブジェクトを使って今後の処理の制御を行なっていきますので、これがないと始まりません。
  2. def.resolve();
    • 処理が正常終了したことを意味します。処理が異常終了したことを意味するreject()もあります。本記事の最後で少し触れていますが、詳しくは別記事にまとめようと思います。
  3. return def.promise();
    • Deferredオブジェクトからresolve()reject()を取り除いたPromiseオブジェクトというものを返しています。returnした先でまたresolve()reject()を使って結果を書き換えられたくない為、このようなオブジェクトが用意されています。

では準備が整ったので、実際に並列処理を実行してみたいと思います。並列処理の書き方はこんな感じ。
↓↓↓

myApp.js
$.when(execA(), execB(), execC())
    .then(execD);

では実際に実行してみましょう。
気になる結果は、
↓↓↓

// 処理B
// 処理A
// 処理C
// 処理D

出来ました!
特に処理を遅らせていない処理Dがしっかりと最後に実行されているのが確認出来ました。とにかく便利なので、ここぞという時には是非使ってみてください。

余談

また、ここからは完全に余談ですが、実行する際のコードを以下のように書くと、resolve()reject()が何をしているかイメージが付きやすいかもしれません。
↓↓↓

myApp.js
$.when(execA(), execB(), execC())
    .then(execD)
    .done(function() {console.log('成功')})       // 追加
    .fail(function() {console.log('失敗')});      // 追加

これは処理Aから処理Cが全て正常終了なら「成功」、どれかが異常終了した場合は「失敗」がコンソールに吐かれます。
今は、全ての処理をresolve()で返しているので「成功」が出て来るかと思いますが、どこかの処理のresolve()reject()に書き換えてみると、「失敗」が吐かれるかと思います。興味がある方は是非やってみてください。

以上、余談でした。

最後まで見て頂き、ありがとうございます。